Skip to content

Commit b253155

Browse files
committed
Fix Windows-incompatible Python detection in venv installer
- shutil.which("python3") → use "python" on Windows (no python3.exe) - Extract _find_base_python() helper shared by both installer paths: - Unix: login-shell probe via bash/zsh (guarded by sys.platform check) - Windows: conda/system paths use python.exe under AppData and C:\ - Removes ~50 lines of duplicated detection logic - Error hint text is now platform-aware (Windows path vs Unix path)
1 parent 6e4d341 commit b253155

1 file changed

Lines changed: 100 additions & 106 deletions

File tree

gui.py

Lines changed: 100 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
PYTHON = ML_VENV_PYTHON
3636
elif getattr(sys, "frozen", False):
3737
import shutil as _shutil
38-
PYTHON = _shutil.which("python3") or _shutil.which("python") or "python3"
38+
if sys.platform == "win32":
39+
PYTHON = _shutil.which("python") or "python"
40+
else:
41+
PYTHON = _shutil.which("python3") or _shutil.which("python") or "python3"
3942
else:
4043
PYTHON = sys.executable
4144

@@ -113,6 +116,88 @@ def _script(name: str) -> Path:
113116
]
114117

115118

119+
def _find_base_python(log_fn: callable) -> str:
120+
"""Find a Python 3 with ssl+venv support.
121+
122+
Tries (in order):
123+
1. Login-shell probe via bash/zsh (Unix only)
124+
2. Common conda/system install locations (platform-specific)
125+
Returns the executable path or '' if nothing is found.
126+
"""
127+
import shutil as _sh
128+
home = Path.home()
129+
130+
# 1) Login shell probe — bash/zsh don't exist on Windows
131+
if sys.platform != "win32":
132+
log_fn("► Probing login shell for Python …")
133+
for shell_cmd in [
134+
["bash", "-l", "-c",
135+
"python3 -c 'import ssl,venv,sys; print(sys.executable)'"],
136+
["zsh", "-l", "-c",
137+
"python3 -c 'import ssl,venv,sys; print(sys.executable)'"],
138+
]:
139+
try:
140+
r = subprocess.run(shell_cmd, capture_output=True,
141+
text=True, timeout=15)
142+
if r.returncode == 0:
143+
for line in reversed(r.stdout.strip().splitlines()):
144+
line = line.strip()
145+
if line and Path(line).exists():
146+
return line
147+
except Exception:
148+
pass
149+
150+
# 2) Common install locations (platform-specific)
151+
if sys.platform == "win32":
152+
_candidates = [
153+
str(home / "miniconda3" / "python.exe"),
154+
str(home / "Miniconda3" / "python.exe"),
155+
str(home / "anaconda3" / "python.exe"),
156+
str(home / "Anaconda3" / "python.exe"),
157+
str(home / "miniforge3" / "python.exe"),
158+
str(home / "mambaforge" / "python.exe"),
159+
# Standard Windows Python installer locations
160+
str(home / "AppData" / "Local" / "Programs" / "Python" / "Python313" / "python.exe"),
161+
str(home / "AppData" / "Local" / "Programs" / "Python" / "Python312" / "python.exe"),
162+
str(home / "AppData" / "Local" / "Programs" / "Python" / "Python311" / "python.exe"),
163+
str(home / "AppData" / "Local" / "Programs" / "Python" / "Python310" / "python.exe"),
164+
str(Path("C:/miniconda3/python.exe")),
165+
str(Path("C:/anaconda3/python.exe")),
166+
str(Path("C:/ProgramData/miniconda3/python.exe")),
167+
str(Path("C:/ProgramData/anaconda3/python.exe")),
168+
_sh.which("python") or "",
169+
]
170+
else:
171+
_candidates = [
172+
str(home / "miniconda3/bin/python3"),
173+
str(home / "miniconda3/bin/python"),
174+
str(home / "anaconda3/bin/python3"),
175+
str(home / "anaconda3/bin/python"),
176+
str(home / "miniforge3/bin/python3"),
177+
str(home / "miniforge3/bin/python"),
178+
str(home / "mambaforge/bin/python3"),
179+
str(home / "mambaforge/bin/python"),
180+
str(home / ".local/share/mamba/bin/python3"),
181+
"/opt/conda/bin/python3",
182+
"/opt/miniconda3/bin/python3",
183+
"/opt/anaconda3/bin/python3",
184+
_sh.which("python3") or "",
185+
_sh.which("python") or "",
186+
"/usr/bin/python3",
187+
"/usr/local/bin/python3",
188+
]
189+
190+
for cand in _candidates:
191+
if not cand or not Path(cand).exists():
192+
continue
193+
r = subprocess.run([cand, "-c", "import ssl, venv"],
194+
capture_output=True, timeout=5)
195+
if r.returncode == 0:
196+
return cand
197+
198+
return ""
199+
200+
116201
def _detect_cuda() -> tuple[int, int] | None:
117202
"""Return (major, minor) CUDA version from nvidia-smi, or None if no GPU."""
118203
try:
@@ -1485,69 +1570,22 @@ def _worker():
14851570
global PYTHON
14861571

14871572
# ── Find a Python that has SSL (required for pip HTTPS) ──────────
1488-
import shutil as _sh
1489-
home = Path.home()
1490-
14911573
# 1) Manual override from the text field
14921574
base_py = _base_py_v["v"].strip()
14931575

1494-
# 2) Login shell — respects .bashrc/.zshrc and conda init
1495-
if not base_py:
1496-
_append_log("► Probing login shell for Python …")
1497-
for shell_cmd in [
1498-
["bash", "-l", "-c",
1499-
"python3 -c 'import ssl,venv,sys; print(sys.executable)'"],
1500-
["zsh", "-l", "-c",
1501-
"python3 -c 'import ssl,venv,sys; print(sys.executable)'"],
1502-
]:
1503-
try:
1504-
r = subprocess.run(shell_cmd, capture_output=True,
1505-
text=True, timeout=15)
1506-
if r.returncode == 0:
1507-
for line in reversed(r.stdout.strip().splitlines()):
1508-
line = line.strip()
1509-
if line and Path(line).exists():
1510-
base_py = line
1511-
break
1512-
if base_py:
1513-
break
1514-
except Exception:
1515-
pass
1516-
1517-
# 3) Common install locations
1576+
# 2) Auto-detect via login shell + common install locations
15181577
if not base_py:
1519-
_candidates = [
1520-
str(home / "miniconda3/bin/python3"),
1521-
str(home / "miniconda3/bin/python"),
1522-
str(home / "anaconda3/bin/python3"),
1523-
str(home / "anaconda3/bin/python"),
1524-
str(home / "miniforge3/bin/python3"),
1525-
str(home / "miniforge3/bin/python"),
1526-
str(home / "mambaforge/bin/python3"),
1527-
str(home / "mambaforge/bin/python"),
1528-
str(home / ".local/share/mamba/bin/python3"),
1529-
"/opt/conda/bin/python3",
1530-
"/opt/miniconda3/bin/python3",
1531-
"/opt/anaconda3/bin/python3",
1532-
_sh.which("python3") or "",
1533-
_sh.which("python") or "",
1534-
"/usr/bin/python3",
1535-
"/usr/local/bin/python3",
1536-
]
1537-
for cand in _candidates:
1538-
if not cand or not Path(cand).exists():
1539-
continue
1540-
r = subprocess.run([cand, "-c", "import ssl, venv"],
1541-
capture_output=True, timeout=5)
1542-
if r.returncode == 0:
1543-
base_py = cand
1544-
break
1578+
base_py = _find_base_python(_append_log)
15451579

15461580
if not base_py:
15471581
_append_log("ERROR: Could not find a Python 3 with SSL support.")
15481582
_append_log("")
1549-
_append_log(" Paste the path to your conda/system Python below")
1550-
_append_log(" (e.g. /home/user/miniconda3/bin/python3)")
1583+
if sys.platform == "win32":
1584+
_append_log(" Paste the path to your Python below")
1585+
_append_log(r" (e.g. C:\Users\you\miniconda3\python.exe)")
1586+
else:
1587+
_append_log(" Paste the path to your conda/system Python below")
1588+
_append_log(" (e.g. /home/user/miniconda3/bin/python3)")
15511589
_append_log(" then click Install again.")
15521590
tf_base_py.visible = True
15531591
env_status.value = "✗ Python not found — enter path below"
@@ -1947,62 +1985,18 @@ def _run_install(_=None) -> None:
19471985
page.update()
19481986

19491987
def _worker():
1950-
import shutil as _sh
1951-
home = Path.home()
19521988
base_py = _base_py_v2["v"].strip()
19531989

19541990
if not base_py:
1955-
_append("► Probing login shell for Python …")
1956-
for shell_cmd in [
1957-
["bash", "-l", "-c",
1958-
"python3 -c 'import ssl,venv,sys; print(sys.executable)'"],
1959-
["zsh", "-l", "-c",
1960-
"python3 -c 'import ssl,venv,sys; print(sys.executable)'"],
1961-
]:
1962-
try:
1963-
r = subprocess.run(shell_cmd, capture_output=True,
1964-
text=True, timeout=15)
1965-
if r.returncode == 0:
1966-
for line in reversed(r.stdout.strip().splitlines()):
1967-
line = line.strip()
1968-
if line and Path(line).exists():
1969-
base_py = line
1970-
break
1971-
if base_py:
1972-
break
1973-
except Exception:
1974-
pass
1975-
1976-
if not base_py:
1977-
for cand in [
1978-
str(home / "miniconda3/bin/python3"),
1979-
str(home / "miniconda3/bin/python"),
1980-
str(home / "anaconda3/bin/python3"),
1981-
str(home / "anaconda3/bin/python"),
1982-
str(home / "miniforge3/bin/python3"),
1983-
str(home / "miniforge3/bin/python"),
1984-
str(home / "mambaforge/bin/python3"),
1985-
str(home / "mambaforge/bin/python"),
1986-
str(home / ".local/share/mamba/bin/python3"),
1987-
"/opt/conda/bin/python3",
1988-
"/opt/miniconda3/bin/python3",
1989-
"/opt/anaconda3/bin/python3",
1990-
_sh.which("python3") or "",
1991-
_sh.which("python") or "",
1992-
"/usr/bin/python3",
1993-
"/usr/local/bin/python3",
1994-
]:
1995-
if not cand or not Path(cand).exists():
1996-
continue
1997-
r = subprocess.run([cand, "-c", "import ssl, venv"],
1998-
capture_output=True, timeout=5)
1999-
if r.returncode == 0:
2000-
base_py = cand
2001-
break
1991+
base_py = _find_base_python(_append)
20021992

20031993
if not base_py:
20041994
_append("ERROR: No Python 3 with SSL support found.")
2005-
_append(" Paste your Python path in the field below and click Install again.")
1995+
if sys.platform == "win32":
1996+
_append(r" Paste your Python path below (e.g. C:\Users\you\miniconda3\python.exe)")
1997+
else:
1998+
_append(" Paste your Python path below (e.g. /home/user/miniconda3/bin/python3)")
1999+
_append(" then click Install again.")
20062000
tf_base_py2.visible = True
20072001
inst_status.value = "✗ Python not found — enter path below"
20082002
inst_status.color = C_ERROR

0 commit comments

Comments
 (0)