Skip to content

Commit afb3a8d

Browse files
committed
Update virtual environment creation in ubuntu and other linux based systems.
Signed-off-by: Rahul Krishna <[email protected]>
1 parent da1c8a7 commit afb3a8d

File tree

3 files changed

+139
-5
lines changed

3 files changed

+139
-5
lines changed

README.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,46 @@ This project uses [uv](https://docs.astral.sh/uv/) for dependency management.
1616
uv python install 3.12
1717
```
1818

19+
#### System Package Requirements
20+
21+
The tool creates virtual environments internally using Python's built-in `venv` module.
22+
23+
**Ubuntu/Debian systems:**
24+
```bash
25+
sudo apt update
26+
sudo apt install python3.12-venv python3-dev build-essential
27+
```
28+
29+
**Fedora/RHEL/CentOS systems:**
30+
```bash
31+
sudo dnf group install "Development Tools"
32+
sudo dnf install python3-pip python3-venv python3-devel
33+
```
34+
or on older versions:
35+
```bash
36+
sudo yum groupinstall "Development Tools"
37+
sudo yum install python3-pip python3-venv python3-devel
38+
```
39+
40+
**macOS systems:**
41+
```bash
42+
# Install Xcode Command Line Tools (for compilation)
43+
xcode-select --install
44+
45+
# If using Homebrew Python (recommended)
46+
brew install [email protected]
47+
48+
# If using pyenv (popular Python version manager)
49+
# First ensure pyenv is properly installed and configured
50+
pyenv install 3.12.0 # or latest 3.12.x version
51+
pyenv global 3.12.0 # or pyenv local 3.12.0 for project-specific
52+
53+
# If using system Python, you may need to install certificates
54+
/Applications/Python\ 3.12/Install\ Certificates.command
55+
```
56+
57+
> **Note:** These packages are required as the tool uses Python's built-in `venv` module to create isolated environments for analysis.
58+
1959
### Setup
2060

2161
1. Clone the repository:

src/codeanalyzer/core.py

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import hashlib
2+
import os
23
from pdb import set_trace
34
import shutil
45
import subprocess
@@ -94,9 +95,11 @@ def _cmd_exec_helper(
9495
returncode = process.wait()
9596

9697
if check and returncode != 0:
97-
raise subprocess.CalledProcessError(
98-
returncode, cmd, output="\n".join(output_lines)
99-
)
98+
error_output = "\n".join(output_lines)
99+
logger.error(f"Command failed with exit code {returncode}: {' '.join(cmd)}")
100+
if error_output:
101+
logger.error(f"Command output:\n{error_output}")
102+
raise subprocess.CalledProcessError(returncode, cmd, output=error_output)
100103

101104
return subprocess.CompletedProcess(
102105
args=cmd,
@@ -105,6 +108,97 @@ def _cmd_exec_helper(
105108
stderr=None,
106109
)
107110

111+
@staticmethod
112+
def _get_base_interpreter() -> Path:
113+
"""Get the base Python interpreter path.
114+
115+
This method finds a suitable base Python interpreter that can be used
116+
to create virtual environments, even when running from within a virtual environment.
117+
It supports various Python version managers like pyenv, conda, asdf, etc.
118+
119+
Returns:
120+
Path: The base Python interpreter path.
121+
122+
Raises:
123+
RuntimeError: If no suitable Python interpreter can be found.
124+
"""
125+
# If we're not in a virtual environment, use the current interpreter
126+
if sys.prefix == sys.base_prefix:
127+
return Path(sys.executable)
128+
129+
# We're inside a virtual environment; need to find the base interpreter
130+
131+
# First, check if user explicitly set SYSTEM_PYTHON
132+
if system_python := os.getenv("SYSTEM_PYTHON"):
133+
system_python_path = Path(system_python)
134+
if system_python_path.exists() and system_python_path.is_file():
135+
return system_python_path
136+
137+
# Try to get the base interpreter from sys.base_executable (Python 3.3+)
138+
if hasattr(sys, "base_executable") and sys.base_executable:
139+
base_exec = Path(sys.base_executable)
140+
if base_exec.exists() and base_exec.is_file():
141+
return base_exec
142+
143+
# Try to find Python interpreters using shlex.which
144+
python_candidates = []
145+
146+
# Use shutil.which to find python3 and python in PATH
147+
for python_name in ["python3", "python"]:
148+
if python_path := shutil.which(python_name):
149+
candidate = Path(python_path)
150+
# Skip if this is the current virtual environment's python
151+
if not str(candidate).startswith(sys.prefix):
152+
python_candidates.append(candidate)
153+
154+
# Check pyenv installation
155+
if pyenv_root := os.getenv("PYENV_ROOT"):
156+
pyenv_python = Path(pyenv_root) / "shims" / "python"
157+
if pyenv_python.exists():
158+
python_candidates.append(pyenv_python)
159+
160+
# Check default pyenv location
161+
home_pyenv = Path.home() / ".pyenv" / "shims" / "python"
162+
if home_pyenv.exists():
163+
python_candidates.append(home_pyenv)
164+
165+
# Check conda base environment
166+
if conda_prefix := os.getenv(
167+
"CONDA_PREFIX_1"
168+
): # Original conda env before activation
169+
conda_python = Path(conda_prefix) / "bin" / "python"
170+
if conda_python.exists():
171+
python_candidates.append(conda_python)
172+
173+
# Check asdf
174+
if asdf_dir := os.getenv("ASDF_DIR"):
175+
asdf_python = Path(asdf_dir) / "shims" / "python"
176+
if asdf_python.exists():
177+
python_candidates.append(asdf_python)
178+
179+
# Test candidates to find a working Python interpreter
180+
for candidate in python_candidates:
181+
try:
182+
# Test if the interpreter works and can create venv
183+
result = subprocess.run(
184+
[str(candidate), "-c", "import venv; print('OK')"],
185+
capture_output=True,
186+
text=True,
187+
timeout=5,
188+
)
189+
if result.returncode == 0 and "OK" in result.stdout:
190+
return candidate
191+
except (subprocess.TimeoutExpired, FileNotFoundError, PermissionError):
192+
continue
193+
194+
# If nothing works, raise an informative error
195+
raise RuntimeError(
196+
f"Could not find a suitable base Python interpreter. "
197+
f"Current environment: {sys.executable} (prefix: {sys.prefix}). "
198+
f"Please set the SYSTEM_PYTHON environment variable to point to "
199+
f"a working Python interpreter that can create virtual environments."
200+
)
201+
108202
def __enter__(self) -> "AnalyzerCore":
109203
# If no virtualenv is provided, try to create one using requirements.txt or pyproject.toml
110204
venv_path = self.cache_dir / self.project_dir.name / "virtualenv"
@@ -114,7 +208,7 @@ def __enter__(self) -> "AnalyzerCore":
114208
if not venv_path.exists() or self.rebuild_analysis:
115209
logger.info(f"(Re-)creating virtual environment at {venv_path}")
116210
self._cmd_exec_helper(
117-
[sys.executable, "-m", "venv", str(venv_path)],
211+
[str(self._get_base_interpreter()), "-m", "venv", str(venv_path)],
118212
check=True,
119213
)
120214
# Find python in the virtual environment

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)