Coverage for main/utils.py: 87%
70 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 15:51 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 15:51 +0000
1import os
2import shutil
3import subprocess
4import platform
5import sys
6from PyQt6.QtCore import QStandardPaths, QCoreApplication
7from pathlib import Path
10def get_bin_path(name):
11 """
12 Locate the executable path for a given binary name across different environments.
14 This function attempts to find the full path of an executable following these steps:
15 1. Check if running from a PyInstaller bundle (using `sys._MEIPASS`).
16 2. Check if the binary exists in the same directory as the current script.
17 3. Search the system PATH using `shutil.which()`.
19 Args:
20 name (str): The base name of the executable (without extension).
22 Returns:
23 str: The absolute path to the executable.
25 Raises:
26 FileNotFoundError: If the executable cannot be found.
27 """
28 if not name or not str(name).strip():
29 raise ValueError(QCoreApplication.translate("Utils", "Tool name not provided"))
31 exe_name = f"{name}.exe" if platform.system() == "Windows" else name
33 # Case 1: Running from a PyInstaller bundle
34 if hasattr(sys, "_MEIPASS"):
35 candidate = os.path.join(sys._MEIPASS, name, exe_name)
36 if os.path.exists(candidate):
37 return candidate
39 # Case 2: Located next to the script or executable
40 candidate = os.path.join(os.path.dirname(__file__), name, exe_name)
41 if os.path.exists(candidate):
42 return candidate
44 # Case 3: Found in the system PATH
45 path = shutil.which(exe_name)
46 if path:
47 return path
49 raise FileNotFoundError(QCoreApplication.translate("Utils", "Could not find executable: {exe_name}").format(exe_name=exe_name))
52def get_app_dir():
53 """
54 Get the writable directory used to store user-specific application data.
56 On all operating systems, this directory is located under the user's home folder
57 (e.g., `~/GliAAns-UI` on Linux and macOS, or `C:\\Users\\<User>\\GliAAns-UI` on Windows).
59 Returns:
60 pathlib.Path: The absolute path to the application data directory.
61 """
62 base = Path(QStandardPaths.writableLocation(QStandardPaths.StandardLocation.HomeLocation)) / "GliAAns-UI"
64 try:
65 base.mkdir(parents=True, exist_ok=True)
66 except PermissionError:
67 raise
68 except OSError as e:
69 # Errno 30 = read-only filesystem, 13 = forbidden
70 if getattr(e, "errno", None) in (13, 30):
71 raise PermissionError(QCoreApplication.translate("Utils", "Error while creating the app working directory")) from e
72 raise
74 return base
77def resource_path(relative_path):
78 """
79 Resolve the absolute path to a resource file.
81 Works in both development environments and PyInstaller-built executables.
83 Args:
84 relative_path (str): The relative path to the resource file.
86 Returns:
87 str: The absolute path to the resource file.
88 """
89 if hasattr(sys, "_MEIPASS"):
90 return os.path.join(sys._MEIPASS, relative_path)
91 return os.path.join(os.path.dirname(__file__), relative_path)
94def get_shell_path():
95 """
96 Retrieve the system PATH from the user's login shell environment.
98 This is necessary because on Unix-based systems (macOS, Linux), GUI applications
99 do not always inherit the user's shell environment (especially when launched from
100 the desktop or a shortcut).
102 Behavior by platform:
103 - **macOS:** Uses the default login shell (`zsh`) to read PATH.
104 - **Linux:** Reads from the user's current shell (usually `bash`).
105 - **Windows:** Returns the PATH from the current environment.
107 Returns:
108 str: The PATH environment variable as a string.
109 """
110 system = platform.system()
112 try:
113 if system == "Darwin": # macOS
114 result = subprocess.run(
115 ["/bin/zsh", "-l", "-c", "echo $PATH"],
116 capture_output=True,
117 text=True,
118 check=True
119 )
120 return result.stdout.strip()
122 elif system == "Linux": # Linux
123 shell = os.environ.get("SHELL", "/bin/bash")
124 result = subprocess.run(
125 [shell, "-l", "-c", "echo $PATH"],
126 capture_output=True,
127 text=True,
128 check=True
129 )
130 return result.stdout.strip()
132 elif system == "Windows": # Windows
133 return os.environ.get("PATH", "")
135 # Fallback for unknown platforms
136 return os.environ.get("PATH", "")
138 except Exception as e:
139 print(f"Error while retrieving PATH from shell: {e}")
140 return os.environ.get("PATH", "")
143def setup_fsl_env():
144 """
145 Load environment variables for FSL (FMRIB Software Library).
147 This function runs a shell command to source the FSL configuration script
148 (`fsl.sh`) and extract the environment variables `FSLDIR` and `FSLOUTPUTTYPE`.
150 Returns:
151 tuple[str, str]: A tuple containing:
152 - `FSLDIR`: The root installation directory of FSL.
153 - `FSLOUTPUTTYPE`: The default FSL output format (e.g., NIFTI_GZ).
155 Raises:
156 RuntimeError: If FSLDIR or FSLOUTPUTTYPE cannot be retrieved.
157 """
158 result = subprocess.run(
159 ["/bin/zsh", "-l", "-c", "source $FSLDIR/etc/fslconf/fsl.sh && echo $FSLDIR && echo $FSLOUTPUTTYPE"],
160 capture_output=True,
161 text=True,
162 check=True
163 )
165 lines = result.stdout.strip().splitlines()
166 if len(lines) < 2:
167 raise RuntimeError("Could not read FSLDIR and FSLOUTPUTTYPE")
169 fsldir, fsloutputtype = lines[0], lines[1]
170 return fsldir, fsloutputtype
172def get_dl_python_executable():
173 """Returns the path of the Python in venv deep learning."""
174 base_dir = Path(sys.executable).parent
176 python_path = base_dir / ".venv-dl" / "bin" / "python"
178 if not python_path.exists():
179 raise FileNotFoundError(f"Python DL environment not found: {python_path}")
181 return str(python_path)
183def get_script_path(script_relative_path):
184 """Returns the absolute path of a deep learning script."""
185 base_dir = Path(sys.executable).parent
186 script_path = base_dir / script_relative_path
187 return str(script_path)