-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy path_packages.py
More file actions
123 lines (96 loc) · 3.87 KB
/
Copy path_packages.py
File metadata and controls
123 lines (96 loc) · 3.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
"""Package installation and environment variable configuration.
Handles installing missing packages via uv, setting pipeline environment
variables, and validating uv sync results.
"""
import os
import subprocess
from pathlib import Path
from infrastructure.core.logging.utils import get_logger, log_success
from infrastructure.core.runtime._python_env import check_uv_available, get_subprocess_env
logger = get_logger(__name__)
def install_missing_packages(packages: list[str], cwd: Path | None = None) -> bool:
"""Install packages with ``uv pip install`` when uv is available.
Args:
packages: Package names/specifiers for pip.
cwd: Working directory for the subprocess (optional).
Returns:
True on success, False if uv is missing or the install command fails.
"""
if not packages:
return True
if not check_uv_available():
logger.warning("install_missing_packages: uv not on PATH")
return False
cmd = ["uv", "pip", "install", *packages]
try:
result = subprocess.run(
cmd,
cwd=str(cwd) if cwd is not None else None,
capture_output=True,
text=True,
check=False,
timeout=300,
env=get_subprocess_env(),
)
except (subprocess.SubprocessError, subprocess.TimeoutExpired) as e:
logger.error(f"install_missing_packages failed: {e}", exc_info=True)
return False
if result.returncode != 0:
logger.error(
"install_missing_packages: uv pip install failed: %s",
(result.stderr or result.stdout or "").strip() or "(no output)",
)
return False
log_success(f"Installed packages: {', '.join(packages)}", logger)
return True
def set_environment_variables(repo_root: Path) -> bool:
"""Configure environment variables for pipeline.
Sets MPLBACKEND=Agg (headless matplotlib), PYTHONIOENCODING=utf-8,
and PROJECT_ROOT for the pipeline scripts.
Args:
repo_root: Repository root directory
Returns:
True if environment variables set successfully, False otherwise
"""
import tempfile
# Set matplotlib backend for headless operation
os.environ["MPLBACKEND"] = "Agg"
# Ensure UTF-8 encoding
os.environ["PYTHONIOENCODING"] = "utf-8"
# Set project root in environment
os.environ["PROJECT_ROOT"] = str(repo_root)
# Set cache dirs to temporary directories to bypass macOS sandbox restrictions
os.environ.setdefault("MPLCONFIGDIR", os.path.join(tempfile.gettempdir(), "matplotlib"))
os.environ.setdefault("UV_CACHE_DIR", os.path.join(tempfile.gettempdir(), "uv_cache"))
log_success(
"Environment variables configured (MPLBACKEND, PYTHONIOENCODING, PROJECT_ROOT, MPLCONFIGDIR, UV_CACHE_DIR)",
logger,
)
return True
def validate_uv_sync_result(repo_root: Path) -> tuple[bool, str]:
"""Check for .venv/ and uv.lock after uv sync; returns (success, message)."""
# Check for .venv directory
venv_path = repo_root / ".venv"
if not venv_path.exists():
return False, "Virtual environment not created"
# Check for uv.lock file
lock_file = repo_root / "uv.lock"
if not lock_file.exists():
return False, "Lock file not generated"
return True, "uv sync completed successfully"
def check_build_tools(tools: dict[str, str]) -> bool:
"""Return True if every named executable is on PATH.
Args:
tools: Map of executable name to short human-readable purpose (for logging).
Returns:
True when all tools are found, False if any are missing.
"""
import shutil
all_ok = True
for name, purpose in tools.items():
if shutil.which(name):
logger.debug(f"Build tool OK: {name} ({purpose})")
else:
logger.warning(f"Build tool missing: {name} ({purpose})")
all_ok = False
return all_ok