expops 0.1.3__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 (86) hide show
  1. expops-0.1.3.dist-info/METADATA +826 -0
  2. expops-0.1.3.dist-info/RECORD +86 -0
  3. expops-0.1.3.dist-info/WHEEL +5 -0
  4. expops-0.1.3.dist-info/entry_points.txt +3 -0
  5. expops-0.1.3.dist-info/licenses/LICENSE +674 -0
  6. expops-0.1.3.dist-info/top_level.txt +1 -0
  7. mlops/__init__.py +0 -0
  8. mlops/__main__.py +11 -0
  9. mlops/_version.py +34 -0
  10. mlops/adapters/__init__.py +12 -0
  11. mlops/adapters/base.py +86 -0
  12. mlops/adapters/config_schema.py +89 -0
  13. mlops/adapters/custom/__init__.py +3 -0
  14. mlops/adapters/custom/custom_adapter.py +447 -0
  15. mlops/adapters/plugin_manager.py +113 -0
  16. mlops/adapters/sklearn/__init__.py +3 -0
  17. mlops/adapters/sklearn/adapter.py +94 -0
  18. mlops/cluster/__init__.py +3 -0
  19. mlops/cluster/controller.py +496 -0
  20. mlops/cluster/process_runner.py +91 -0
  21. mlops/cluster/providers.py +258 -0
  22. mlops/core/__init__.py +95 -0
  23. mlops/core/custom_model_base.py +38 -0
  24. mlops/core/dask_networkx_executor.py +1265 -0
  25. mlops/core/executor_worker.py +1239 -0
  26. mlops/core/experiment_tracker.py +81 -0
  27. mlops/core/graph_types.py +64 -0
  28. mlops/core/networkx_parser.py +135 -0
  29. mlops/core/payload_spill.py +278 -0
  30. mlops/core/pipeline_utils.py +162 -0
  31. mlops/core/process_hashing.py +216 -0
  32. mlops/core/step_state_manager.py +1298 -0
  33. mlops/core/step_system.py +956 -0
  34. mlops/core/workspace.py +99 -0
  35. mlops/environment/__init__.py +10 -0
  36. mlops/environment/base.py +43 -0
  37. mlops/environment/conda_manager.py +307 -0
  38. mlops/environment/factory.py +70 -0
  39. mlops/environment/pyenv_manager.py +146 -0
  40. mlops/environment/setup_env.py +31 -0
  41. mlops/environment/system_manager.py +66 -0
  42. mlops/environment/utils.py +105 -0
  43. mlops/environment/venv_manager.py +134 -0
  44. mlops/main.py +527 -0
  45. mlops/managers/project_manager.py +400 -0
  46. mlops/managers/reproducibility_manager.py +575 -0
  47. mlops/platform.py +996 -0
  48. mlops/reporting/__init__.py +16 -0
  49. mlops/reporting/context.py +187 -0
  50. mlops/reporting/entrypoint.py +292 -0
  51. mlops/reporting/kv_utils.py +77 -0
  52. mlops/reporting/registry.py +50 -0
  53. mlops/runtime/__init__.py +9 -0
  54. mlops/runtime/context.py +34 -0
  55. mlops/runtime/env_export.py +113 -0
  56. mlops/storage/__init__.py +12 -0
  57. mlops/storage/adapters/__init__.py +9 -0
  58. mlops/storage/adapters/gcp_kv_store.py +778 -0
  59. mlops/storage/adapters/gcs_object_store.py +96 -0
  60. mlops/storage/adapters/memory_store.py +240 -0
  61. mlops/storage/adapters/redis_store.py +438 -0
  62. mlops/storage/factory.py +199 -0
  63. mlops/storage/interfaces/__init__.py +6 -0
  64. mlops/storage/interfaces/kv_store.py +118 -0
  65. mlops/storage/path_utils.py +38 -0
  66. mlops/templates/premier-league/charts/plot_metrics.js +70 -0
  67. mlops/templates/premier-league/charts/plot_metrics.py +145 -0
  68. mlops/templates/premier-league/charts/requirements.txt +6 -0
  69. mlops/templates/premier-league/configs/cluster_config.yaml +13 -0
  70. mlops/templates/premier-league/configs/project_config.yaml +207 -0
  71. mlops/templates/premier-league/data/England CSV.csv +12154 -0
  72. mlops/templates/premier-league/models/premier_league_model.py +638 -0
  73. mlops/templates/premier-league/requirements.txt +8 -0
  74. mlops/templates/sklearn-basic/README.md +22 -0
  75. mlops/templates/sklearn-basic/charts/plot_metrics.py +85 -0
  76. mlops/templates/sklearn-basic/charts/requirements.txt +3 -0
  77. mlops/templates/sklearn-basic/configs/project_config.yaml +64 -0
  78. mlops/templates/sklearn-basic/data/train.csv +14 -0
  79. mlops/templates/sklearn-basic/models/model.py +62 -0
  80. mlops/templates/sklearn-basic/requirements.txt +10 -0
  81. mlops/web/__init__.py +3 -0
  82. mlops/web/server.py +585 -0
  83. mlops/web/ui/index.html +52 -0
  84. mlops/web/ui/mlops-charts.js +357 -0
  85. mlops/web/ui/script.js +1244 -0
  86. mlops/web/ui/styles.css +248 -0
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import sys
6
+ from pathlib import Path
7
+
8
+
9
+ def parse_args() -> argparse.Namespace:
10
+ p = argparse.ArgumentParser(description="Setup project environment and write interpreter path")
11
+ p.add_argument("--project-id", required=True)
12
+ p.add_argument("--project-dir", required=True, help="Path to repository root")
13
+ p.add_argument("--env-file", required=True, help="Path to write the interpreter path")
14
+ return p.parse_args()
15
+
16
+
17
+ def main() -> None:
18
+ args = parse_args()
19
+ repo_root = Path(args.project_dir).resolve()
20
+ src_path = repo_root / "src"
21
+ if str(src_path) not in sys.path:
22
+ sys.path.insert(0, str(src_path))
23
+
24
+ from mlops.core.pipeline_utils import setup_environment_and_write_interpreter
25
+
26
+ py = setup_environment_and_write_interpreter(repo_root, args.project_id, args.env_file)
27
+ print(f"[env] Environment setup completed. Interpreter: {py}")
28
+
29
+
30
+ if __name__ == "__main__":
31
+ main()
@@ -0,0 +1,66 @@
1
+ from __future__ import annotations
2
+
3
+ import subprocess
4
+ import sys
5
+ from typing import Any
6
+
7
+ from .base import EnvironmentManager
8
+ from .utils import load_requirements, verify_pip_requirements
9
+
10
+
11
+ class SystemEnvironmentManager(EnvironmentManager):
12
+ """System Python environment management (no virtual environment)."""
13
+
14
+ def __init__(self, config: dict[str, Any]):
15
+ super().__init__(config)
16
+ self.requirements = load_requirements(config)
17
+ self.environment_name = "system"
18
+ self.python_interpreter = sys.executable
19
+
20
+ def get_environment_type(self) -> str:
21
+ return "system"
22
+
23
+ def get_environment_name(self) -> str:
24
+ return self.environment_name
25
+
26
+ def get_python_interpreter(self) -> str:
27
+ return self.python_interpreter
28
+
29
+ def environment_exists(self) -> bool:
30
+ """System environment always exists."""
31
+ return True
32
+
33
+ def setup_environment(self) -> None:
34
+ """Set up using the system Python environment."""
35
+ print(f"[SystemEnvironmentManager] Using system Python environment...")
36
+
37
+ if self.requirements:
38
+ print(f"[SystemEnvironmentManager] Installing requirements to system Python...")
39
+ try:
40
+ pip_install_cmd = [self.python_interpreter, "-m", "pip", "install"] + self.requirements
41
+ subprocess.run(pip_install_cmd, check=True)
42
+ except subprocess.CalledProcessError as e:
43
+ raise RuntimeError(f"Failed to install requirements to system Python: {e}")
44
+
45
+ print(f"[SystemEnvironmentManager] Python interpreter: {self.python_interpreter}")
46
+
47
+ def verify_environment(self) -> bool:
48
+ """Verify that the system environment is properly configured."""
49
+ print(f"[SystemEnvironmentManager] Verifying system Python environment...")
50
+
51
+ try:
52
+ python_version_output = subprocess.check_output([self.python_interpreter, '--version'], text=True, stderr=subprocess.STDOUT).strip()
53
+ print(f"[SystemEnvironmentManager] Python interpreter is working: {python_version_output}")
54
+ except (subprocess.CalledProcessError, FileNotFoundError) as e:
55
+ print(f"[SystemEnvironmentManager] Python interpreter '{self.python_interpreter}' is not working: {e}")
56
+ return False
57
+
58
+ if self.requirements:
59
+ ok, missing = verify_pip_requirements(self.python_interpreter, self.requirements)
60
+ if not ok:
61
+ print(f"[SystemEnvironmentManager] Missing packages: {', '.join(missing)}")
62
+ print(f"[SystemEnvironmentManager] Environment verification failed for system Python.")
63
+ return False
64
+
65
+ print(f"[SystemEnvironmentManager] Environment verification successful for system Python.")
66
+ return True
@@ -0,0 +1,105 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ import subprocess
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+
9
+ def _read_requirement_lines(path: Path) -> list[str]:
10
+ lines: list[str] = []
11
+ with open(path, "r", encoding="utf-8") as f:
12
+ for raw in f:
13
+ line = raw.strip()
14
+ if not line or line.startswith("#"):
15
+ continue
16
+ lines.append(line)
17
+ return lines
18
+
19
+
20
+ def load_requirements(config: dict[str, Any]) -> list[str]:
21
+ """Load pip-style requirements from a config dict.
22
+
23
+ Supported keys:
24
+ - `requirements`: list[str] or newline-separated str
25
+ - `requirements_file`: path to a requirements.txt file
26
+ """
27
+ if "requirements" in config:
28
+ reqs = config.get("requirements")
29
+ if isinstance(reqs, str):
30
+ out: list[str] = []
31
+ for raw in reqs.splitlines():
32
+ line = raw.strip()
33
+ if not line or line.startswith("#"):
34
+ continue
35
+ out.append(line)
36
+ return out
37
+ if isinstance(reqs, list):
38
+ out: list[str] = []
39
+ for item in reqs:
40
+ s = str(item).strip()
41
+ if s and not s.startswith("#"):
42
+ out.append(s)
43
+ return out
44
+
45
+ req_file = config.get("requirements_file")
46
+ if req_file:
47
+ path = Path(str(req_file))
48
+ if not path.exists():
49
+ raise FileNotFoundError(f"Requirements file not found: {path}")
50
+ return _read_requirement_lines(path)
51
+
52
+ return []
53
+
54
+
55
+ _NAME_RE = re.compile(r"^[A-Za-z0-9_.-]+")
56
+
57
+
58
+ def requirement_to_package_name(requirement: str) -> str:
59
+ """Best-effort extract a package name for `pip show` from a requirement spec."""
60
+ s = str(requirement).strip()
61
+ if not s:
62
+ return ""
63
+
64
+ # Handle "name @ url"
65
+ if "@" in s and not s.startswith("@"):
66
+ s = s.split("@", 1)[0].strip()
67
+
68
+ # Strip environment markers (PEP 508)
69
+ if ";" in s:
70
+ s = s.split(";", 1)[0].strip()
71
+
72
+ # Strip extras
73
+ if "[" in s:
74
+ s = s.split("[", 1)[0].strip()
75
+
76
+ # Strip version specifiers (common cases)
77
+ for op in ("===", "==", ">=", "<=", "!=", "~=", ">", "<", "="):
78
+ if op in s:
79
+ s = s.split(op, 1)[0].strip()
80
+ break
81
+
82
+ m = _NAME_RE.match(s)
83
+ return m.group(0) if m else s
84
+
85
+
86
+ def verify_pip_requirements(python_exec: str, requirements: list[str]) -> tuple[bool, list[str]]:
87
+ """Return (ok, missing_packages) using `pip show`."""
88
+ missing: list[str] = []
89
+ for req in requirements or []:
90
+ name = requirement_to_package_name(req)
91
+ if not name:
92
+ continue
93
+ try:
94
+ subprocess.check_output(
95
+ [python_exec, "-m", "pip", "show", name],
96
+ text=True,
97
+ stderr=subprocess.DEVNULL,
98
+ )
99
+ except subprocess.CalledProcessError:
100
+ missing.append(name)
101
+ except FileNotFoundError:
102
+ missing.append(name)
103
+ return (len(missing) == 0), missing
104
+
105
+
@@ -0,0 +1,134 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from .base import EnvironmentManager
10
+ from .utils import load_requirements, verify_pip_requirements
11
+
12
+
13
+ class VenvEnvironmentManager(EnvironmentManager):
14
+ """Standard Python venv-based environment management."""
15
+
16
+ def __init__(self, config: dict[str, Any]):
17
+ super().__init__(config)
18
+ self.requirements = load_requirements(config)
19
+
20
+ env_name = config.get("name")
21
+ if not env_name:
22
+ raise ValueError(
23
+ "Virtual environment name must be explicitly specified in the configuration. "
24
+ "Please add 'name' field under 'environment.venv' in your config file."
25
+ )
26
+ self.environment_name = env_name
27
+ # Prefer project-local `.venvs/` but preserve existing cache environments if present.
28
+ preferred_path = Path.cwd() / ".venvs" / self.environment_name
29
+ cache_path = Path.home() / ".cache" / "mlops-platform" / "venvs" / self.environment_name
30
+
31
+ if self._python_path(preferred_path).exists():
32
+ self.venv_path = preferred_path
33
+ elif self._python_path(cache_path).exists():
34
+ self.venv_path = cache_path
35
+ else:
36
+ self.venv_path = preferred_path if self._ensure_writable_dir(preferred_path.parent) else cache_path
37
+
38
+ @staticmethod
39
+ def _python_path(venv_path: Path) -> Path:
40
+ if os.name == "nt":
41
+ return venv_path / "Scripts" / "python.exe"
42
+ return venv_path / "bin" / "python"
43
+
44
+ @staticmethod
45
+ def _ensure_writable_dir(dir_path: Path) -> bool:
46
+ try:
47
+ dir_path.mkdir(parents=True, exist_ok=True)
48
+ probe = dir_path / ".mlops_write_probe"
49
+ probe.write_text("", encoding="utf-8")
50
+ probe.unlink(missing_ok=True)
51
+ return True
52
+ except Exception:
53
+ return False
54
+
55
+ def get_environment_type(self) -> str:
56
+ return "venv"
57
+
58
+ def get_environment_name(self) -> str:
59
+ return self.environment_name
60
+
61
+ def get_python_interpreter(self) -> str:
62
+ if not self.python_interpreter:
63
+ raise RuntimeError("Environment not set up yet. Call setup_environment() first.")
64
+ return self.python_interpreter
65
+
66
+ def environment_exists(self) -> bool:
67
+ """Check if the virtual environment already exists."""
68
+ return self._python_path(self.venv_path).exists()
69
+
70
+ def setup_environment(self) -> None:
71
+ """Set up the virtual environment based on configuration."""
72
+ print(f"[VenvEnvironmentManager] Starting venv environment setup for '{self.environment_name}'...")
73
+
74
+ if not self.environment_exists():
75
+ print(f"[VenvEnvironmentManager] Environment '{self.environment_name}' not found. Creating it...")
76
+
77
+ self.venv_path.parent.mkdir(parents=True, exist_ok=True)
78
+
79
+ try:
80
+ subprocess.run([sys.executable, "-m", "venv", str(self.venv_path)], check=True)
81
+ print(f"[VenvEnvironmentManager] Environment '{self.environment_name}' created successfully.")
82
+ except subprocess.CalledProcessError as e:
83
+ # One more fallback: use virtualenv if available
84
+ try:
85
+ subprocess.run([sys.executable, "-m", "virtualenv", str(self.venv_path)], check=True)
86
+ print(f"[VenvEnvironmentManager] Environment '{self.environment_name}' created via virtualenv.")
87
+ except Exception:
88
+ raise RuntimeError(f"Failed to create virtual environment '{self.environment_name}': {e}")
89
+ else:
90
+ print(f"[VenvEnvironmentManager] Using existing environment: '{self.environment_name}'")
91
+
92
+ self.python_interpreter = str(self._python_path(self.venv_path))
93
+
94
+ if not Path(self.python_interpreter).exists():
95
+ raise FileNotFoundError(f"Python interpreter not found in environment '{self.environment_name}' at expected path: {self.python_interpreter}")
96
+
97
+ if self.requirements:
98
+ print(f"[VenvEnvironmentManager] Installing requirements...")
99
+ try:
100
+ # Ensure modern build tooling to prefer wheels over source builds
101
+ subprocess.run([self.python_interpreter, "-m", "pip", "install", "--upgrade", "pip", "setuptools", "wheel"], check=True)
102
+ pip_install_cmd = [self.python_interpreter, "-m", "pip", "install", "--no-cache-dir"] + self.requirements
103
+ subprocess.run(pip_install_cmd, check=True)
104
+ print(f"[VenvEnvironmentManager] Requirements installed successfully.")
105
+ except subprocess.CalledProcessError as e:
106
+ raise RuntimeError(f"Failed to install requirements: {e}")
107
+
108
+ print(f"[VenvEnvironmentManager] Python interpreter: {self.python_interpreter}")
109
+ print(f"[VenvEnvironmentManager] Environment setup completed for '{self.environment_name}'.")
110
+
111
+ def verify_environment(self) -> bool:
112
+ """Verify that the environment is properly configured."""
113
+ if not self.python_interpreter:
114
+ print("[VenvEnvironmentManager] Python interpreter not set. Cannot verify environment.")
115
+ return False
116
+
117
+ print(f"[VenvEnvironmentManager] Verifying environment '{self.environment_name}'...")
118
+
119
+ try:
120
+ python_version_output = subprocess.check_output([self.python_interpreter, '--version'], text=True, stderr=subprocess.STDOUT).strip()
121
+ print(f"[VenvEnvironmentManager] Python interpreter is working: {python_version_output}")
122
+ except (subprocess.CalledProcessError, FileNotFoundError) as e:
123
+ print(f"[VenvEnvironmentManager] Python interpreter '{self.python_interpreter}' is not working: {e}")
124
+ return False
125
+
126
+ if self.requirements:
127
+ ok, missing = verify_pip_requirements(self.python_interpreter, self.requirements)
128
+ if not ok:
129
+ print(f"[VenvEnvironmentManager] Missing packages: {', '.join(missing)}")
130
+ print(f"[VenvEnvironmentManager] Environment verification failed for '{self.environment_name}'.")
131
+ return False
132
+
133
+ print(f"[VenvEnvironmentManager] Environment verification successful for '{self.environment_name}'.")
134
+ return True