Skip to content

Commit a5c42aa

Browse files
authored
Fix macOS QGIS dependency installer (#11)
* Fix macOS QGIS dependency installer * Bump plugin patch version * Update plugin changelog * Fix plugin changelog formatting * Address dependency installer review feedback
1 parent 4bf84e4 commit a5c42aa

2 files changed

Lines changed: 46 additions & 5 deletions

File tree

plugin_template/deps_manager.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -211,11 +211,32 @@ def _is_python_executable_name(path: str) -> bool:
211211
)
212212

213213

214+
def _is_macos_qgis_app_bundle_python(path: str) -> bool:
215+
"""Return True for unsafe Python launchers in QGIS.app/Contents/MacOS."""
216+
if not (platform.system() == "Darwin" or sys.platform == "darwin"):
217+
return False
218+
parts = os.path.abspath(path).split(os.sep)
219+
for idx, part in enumerate(parts):
220+
lower = part.lower()
221+
if not (lower.startswith("qgis") and lower.endswith(".app")):
222+
continue
223+
if idx + 2 >= len(parts):
224+
return False
225+
if parts[idx + 1].lower() != "contents" or parts[idx + 2].lower() != "macos":
226+
return False
227+
name = os.path.basename(path).lower()
228+
return name.startswith("qgis") or _is_python_executable_name(path)
229+
return False
230+
231+
214232
def _python_candidate_matches_runtime(path: str) -> bool:
215233
"""Return True when a candidate is executable and matches QGIS Python."""
216234
if not path or not os.path.isfile(path) or not _is_python_executable_name(path):
217235
return False
218236

237+
if _is_macos_qgis_app_bundle_python(path):
238+
return False
239+
219240
try:
220241
result = subprocess.run( # nosec B603
221242
[
@@ -503,11 +524,24 @@ def create_venv(venv_dir: str) -> str:
503524
env = _get_clean_env()
504525
kwargs = _get_subprocess_kwargs()
505526

506-
# Strategy 0: Use uv venv when available (fastest, no pip needed)
527+
python_exe = None
528+
python_lookup_error = ""
529+
try:
530+
python_exe = _find_python_executable()
531+
except RuntimeError as exc:
532+
python_lookup_error = str(exc)
533+
534+
# Strategy 0: Use uv venv when available (fastest, no pip needed). On
535+
# official macOS QGIS app bundles, require uv-managed Python instead of
536+
# reusing QGIS's app-bundle Python wrapper for venv creation.
537+
uv_error = ""
507538
if uv_exists():
508539
uv_path = get_uv_path()
509-
python_exe = _find_python_executable()
510-
cmd = [uv_path, "venv", "--python", python_exe, venv_dir]
540+
uv_python = python_exe or f"{sys.version_info.major}.{sys.version_info.minor}"
541+
cmd = [uv_path, "venv"]
542+
if python_exe is None:
543+
cmd.append("--managed-python")
544+
cmd += ["--python", uv_python, venv_dir]
511545
result = subprocess.run( # nosec B603
512546
cmd,
513547
capture_output=True,
@@ -518,12 +552,17 @@ def create_venv(venv_dir: str) -> str:
518552
)
519553
if result.returncode == 0 and os.path.isfile(python_path):
520554
return python_path
555+
uv_error = result.stderr or result.stdout or f"exit code {result.returncode}"
521556
# uv venv failed — clean up and fall through to pip strategies
522557
_cleanup_partial_venv(venv_dir)
523558

524559
# Strategy 1: Subprocess with the real Python executable
525-
python_exe = _find_python_executable()
526560
subprocess_error = ""
561+
if python_exe is None:
562+
message = python_lookup_error or "No usable Python executable was found."
563+
if uv_error:
564+
message += f"\nuv venv failed: {uv_error}"
565+
raise RuntimeError(message)
527566

528567
cmd = [python_exe, "-m", "venv", venv_dir]
529568
result = subprocess.run( # nosec B603

plugin_template/metadata.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name=Plugin Template
33
qgisMinimumVersion=3.28
44
qgisMaximumVersion=4.99
55
description=A template for creating QGIS plugins with dockable panels, update checker, and about dialog.
6-
version=0.3.1
6+
version=0.3.2
77
author=Your Name
88
email=your.email@example.com
99

@@ -38,6 +38,8 @@ experimental=False
3838
deprecated=False
3939

4040
changelog=
41+
0.3.2 - Dependency installer fix
42+
- Fix macOS QGIS installer dependency installation
4143
0.3.1 - Dependency installer fix
4244
- Fixed Python detection for macOS QGIS app bundles
4345
0.3.0 - QGIS 4.0 / Qt6 compatibility

0 commit comments

Comments
 (0)