Skip to content

Commit fe5be35

Browse files
committed
Séquence l'installation des dépendances et borne PySide6 pour Python 3.11
Réorganise ensure_tools_installed() pour exécuter systématiquement l'installation des dépendances système avant l'installation des dépendances Python, afin d'éviter les lancements concurrents qui provoquaient des conflits entre gestionnaire système et pip. En cas d'échec, d'impossibilité de démarrage ou de dépassement de délai sur la phase système, conserve désormais une journalisation explicite de l'erreur mais enchaîne quand même sur la phase Python. La méthode retourne toujours l'état global de la phase système via system_install_ok, ce qui permet de préserver le signal d'échec sans bloquer l'installation Python. Ajoute un test unitaire dédié qui vérifie deux comportements clés : l'ordre d'exécution système puis Python, et la poursuite de l'installation Python après un timeout système. Nettoie requirements.txt en supprimant les doublons PySide6/shiboken6 et en bornant explicitement les versions par branche de Python. Pour Python 3.11, la dépendance est limitée à la ligne 6.4.x (< 6.5) ; pour Python 3.12, la ligne 6.6-6.7 est conservée ; pour Python 3.13 et plus, la ligne 6.8+ reste autorisée. Une note a aussi été ajoutée pour documenter que la compatibilité avec des CPU anciens sans certaines extensions SIMD ne peut pas être garantie uniquement via les wheels PyPI.
1 parent 6a5e0b3 commit fe5be35

3 files changed

Lines changed: 181 additions & 71 deletions

File tree

EngineLoader/base.py

Lines changed: 44 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -178,31 +178,9 @@ def ensure_tools_installed(self, gui) -> bool:
178178
tools = self.required_tools
179179
python_tools = tools.get("python", [])
180180
system_tools = tools.get("system", [])
181-
missing_python: list[str] = []
182-
use_system_python = False
183-
python_venv_path = None
184-
can_install_python = bool(
185-
hasattr(gui, "venv_manager") and gui.venv_manager and python_tools
186-
)
187-
188-
# Collect missing Python tools first; actual installation is executed after system phase.
189-
if can_install_python:
190-
use_system_python = bool(getattr(gui, "use_system_python", False))
191-
if use_system_python:
192-
for tool in python_tools:
193-
if not gui.venv_manager.is_tool_installed_system(tool):
194-
missing_python.append(tool)
195-
else:
196-
python_venv_path = gui.venv_manager.resolve_project_venv()
197-
if python_venv_path:
198-
for tool in python_tools:
199-
if not gui.venv_manager.is_tool_installed(
200-
python_venv_path, tool
201-
):
202-
missing_python.append(tool)
181+
system_install_ok = True
203182

204-
# Check and install system tools first to avoid concurrent installs.
205-
system_phase_ok = True
183+
# Check and install system tools first to avoid overlapping system/Python installs.
206184
if system_tools:
207185
try:
208186
# Import and use SysDependencyManager directly for full GUI support
@@ -259,7 +237,7 @@ def ensure_tools_installed(self, gui) -> bool:
259237
f"Échec installation outils système: {missing_system} (code: {process.exitCode()})",
260238
f"System tools installation failed: {missing_system} (code: {process.exitCode()})",
261239
)
262-
system_phase_ok = False
240+
system_install_ok = False
263241
else:
264242
_log_tools(
265243
gui,
@@ -268,7 +246,7 @@ def ensure_tools_installed(self, gui) -> bool:
268246
"Timeout lors de l'installation des outils système",
269247
"Timeout during system tools installation",
270248
)
271-
system_phase_ok = False
249+
system_install_ok = False
272250
else:
273251
_log_tools(
274252
gui,
@@ -277,7 +255,7 @@ def ensure_tools_installed(self, gui) -> bool:
277255
"Impossible de démarrer l'installation des outils système",
278256
"Unable to start system tools installation",
279257
)
280-
system_phase_ok = False
258+
system_install_ok = False
281259

282260
elif system == "windows":
283261
# Convert package names to winget format for Windows
@@ -326,7 +304,7 @@ def ensure_tools_installed(self, gui) -> bool:
326304
f"Échec installation Windows: {missing_system}",
327305
f"Windows installation failed: {missing_system}",
328306
)
329-
system_phase_ok = False
307+
system_install_ok = False
330308
else:
331309
_log_tools(
332310
gui,
@@ -335,7 +313,7 @@ def ensure_tools_installed(self, gui) -> bool:
335313
"Timeout lors de l'installation Windows",
336314
"Timeout during Windows installation",
337315
)
338-
system_phase_ok = False
316+
system_install_ok = False
339317
else:
340318
_log_tools(
341319
gui,
@@ -350,7 +328,7 @@ def ensure_tools_installed(self, gui) -> bool:
350328
"https://learn.microsoft.com/en-us/windows/package-manager/winget/"
351329
]
352330
)
353-
system_phase_ok = False
331+
system_install_ok = False
354332
else:
355333
_log_tools(
356334
gui,
@@ -367,7 +345,7 @@ def ensure_tools_installed(self, gui) -> bool:
367345
"Plateforme non supportée pour l'installation automatique",
368346
"Platform not supported for automatic installation",
369347
)
370-
system_phase_ok = False
348+
system_install_ok = False
371349
else:
372350
_log_tools(
373351
gui,
@@ -385,43 +363,43 @@ def ensure_tools_installed(self, gui) -> bool:
385363
f"Erreur lors de la vérification/installation des outils système: {e}",
386364
f"Error checking/installing system tools: {e}",
387365
)
388-
system_phase_ok = False
389-
390-
if not system_phase_ok:
391-
_log_tools(
392-
gui,
393-
"warning",
394-
"system",
395-
"Installation système incomplète/échouée; passage à l'installation Python.",
396-
"System installation failed/incomplete; continuing with Python installation.",
397-
)
398-
399-
# Install Python tools only after system phase has ended.
400-
if can_install_python and missing_python:
401-
_log_tools(
402-
gui,
403-
"info",
404-
"python",
405-
f"Installation des outils Python manquants: {missing_python}",
406-
f"Installing missing Python tools: {missing_python}",
407-
)
408-
try:
409-
if use_system_python:
410-
gui.venv_manager.ensure_tools_installed_system(missing_python)
411-
elif python_venv_path:
412-
gui.venv_manager.ensure_tools_installed(
413-
python_venv_path, missing_python
366+
system_install_ok = False
367+
368+
# Check Python tools after the system phase, even if the system phase failed.
369+
if hasattr(gui, "venv_manager") and gui.venv_manager and python_tools:
370+
use_system = bool(getattr(gui, "use_system_python", False))
371+
if use_system:
372+
missing_python = []
373+
for tool in python_tools:
374+
if not gui.venv_manager.is_tool_installed_system(tool):
375+
missing_python.append(tool)
376+
if missing_python:
377+
log_i18n_level(
378+
gui,
379+
"info",
380+
f"Installation des outils Python manquants: {missing_python}",
381+
f"Installing missing Python tools: {missing_python}",
414382
)
415-
except Exception as e:
416-
_log_tools(
417-
gui,
418-
"warning",
419-
"python",
420-
f"Erreur lors de l'installation des outils Python: {e}",
421-
f"Error during Python tools installation: {e}",
422-
)
383+
gui.venv_manager.ensure_tools_installed_system(missing_python)
384+
else:
385+
venv_path = gui.venv_manager.resolve_project_venv()
386+
if venv_path:
387+
missing_python = []
388+
for tool in python_tools:
389+
if not gui.venv_manager.is_tool_installed(venv_path, tool):
390+
missing_python.append(tool)
391+
if missing_python:
392+
log_i18n_level(
393+
gui,
394+
"info",
395+
f"Installation des outils Python manquants: {missing_python}",
396+
f"Installing missing Python tools: {missing_python}",
397+
)
398+
gui.venv_manager.ensure_tools_installed(
399+
venv_path, missing_python
400+
)
423401

424-
return True
402+
return system_install_ok
425403
except Exception as e:
426404
log_i18n_level(
427405
gui,

requirements.txt

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,21 @@
33
# Use with constraints.txt for reproducible builds: pip install -r requirements.txt -c constraints.txt
44

55
# Core GUI framework (Qt for Python)
6-
# PySide6/shiboken6 6.8+ adds Python 3.13 support. Keep 6.7.x for <=3.12.
7-
PySide6
8-
PySide6
9-
shiboken6
10-
shiboken6
6+
# Matrix:
7+
# - Python 3.11 => keep PySide6/shiboken6 < 6.5 as requested
8+
# - Python 3.12 => stay on the Qt 6.7 line for better stability
9+
# - Python >= 3.13 => PySide6/shiboken6 >= 6.8 (adds 3.13 support)
10+
# Note:
11+
# - I found official support for Python 3.11 on the 6.4 line.
12+
# - I did not find a reliable official source proving that a given PyPI wheel avoids SSE/SSE2/SSE4.x requirements.
13+
# - So the < 6.5 pin is a compatibility choice, not a hard guarantee for very old CPUs.
14+
# - On such machines, prefer distro packages or a source build of PySide6/shiboken6.
15+
PySide6>=6.8,<6.11; python_version >= "3.13"
16+
shiboken6>=6.8,<6.11; python_version >= "3.13"
17+
PySide6>=6.6,<6.8; python_version >= "3.12" and python_version < "3.13"
18+
shiboken6>=6.6,<6.8; python_version >= "3.12" and python_version < "3.13"
19+
PySide6>=6.4,<6.5; python_version >= "3.11" and python_version < "3.12"
20+
shiboken6>=6.4,<6.5; python_version >= "3.11" and python_version < "3.12"
1121

1222
# System utilities
1323
psutil>=5.9.0,<6.0.0
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# Copyright 2026 Ague Samuel Amen
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
from __future__ import annotations
17+
18+
from EngineLoader.base import CompilerEngine
19+
20+
21+
class DummyProcess:
22+
def __init__(self, wait_result: bool, exit_code: int) -> None:
23+
self._wait_result = wait_result
24+
self._exit_code = exit_code
25+
26+
def waitForFinished(self, _timeout: int) -> bool:
27+
return self._wait_result
28+
29+
def exitCode(self) -> int:
30+
return self._exit_code
31+
32+
33+
class DummySysDepsManager:
34+
def __init__(self, events: list[str], process: DummyProcess) -> None:
35+
self._events = events
36+
self._process = process
37+
38+
def install_packages_linux(self, packages: list[str]):
39+
self._events.append(f"system-install:{','.join(packages)}")
40+
return self._process
41+
42+
def open_urls(self, _urls: list[str]) -> None:
43+
self._events.append("open-urls")
44+
45+
46+
class DummyVenvManager:
47+
def __init__(self, events: list[str]) -> None:
48+
self._events = events
49+
50+
def resolve_project_venv(self) -> str:
51+
return "/tmp/project/.venv"
52+
53+
def is_tool_installed(self, _venv_path: str, _tool: str) -> bool:
54+
return False
55+
56+
def ensure_tools_installed(self, venv_path: str, tools: list[str]) -> None:
57+
self._events.append(f"python-install:{venv_path}:{','.join(tools)}")
58+
59+
60+
class DummyGui:
61+
def __init__(self, events: list[str]) -> None:
62+
self.current_language = "fr"
63+
self.log: list[str] = []
64+
self.venv_manager = DummyVenvManager(events)
65+
self.use_system_python = False
66+
67+
68+
class DummyEngine(CompilerEngine):
69+
@property
70+
def required_tools(self) -> dict[str, list[str]]:
71+
return {"system": ["patchelf"], "python": ["nuitka"]}
72+
73+
74+
def test_ensure_tools_installed_runs_system_before_python(monkeypatch) -> None:
75+
events: list[str] = []
76+
process = DummyProcess(wait_result=True, exit_code=0)
77+
78+
import Core.sys_deps as sys_deps
79+
80+
monkeypatch.setattr(sys_deps, "check_system_packages", lambda _packages: False)
81+
monkeypatch.setattr(
82+
sys_deps,
83+
"SysDependencyManager",
84+
lambda gui: DummySysDepsManager(events, process),
85+
)
86+
monkeypatch.setattr("platform.system", lambda: "Linux")
87+
88+
gui = DummyGui(events)
89+
engine = DummyEngine()
90+
91+
assert engine.ensure_tools_installed(gui) is True
92+
assert events == [
93+
"system-install:patchelf",
94+
"python-install:/tmp/project/.venv:nuitka",
95+
]
96+
97+
98+
def test_ensure_tools_installed_continues_python_after_system_timeout(
99+
monkeypatch,
100+
) -> None:
101+
events: list[str] = []
102+
process = DummyProcess(wait_result=False, exit_code=1)
103+
104+
import Core.sys_deps as sys_deps
105+
106+
monkeypatch.setattr(sys_deps, "check_system_packages", lambda _packages: False)
107+
monkeypatch.setattr(
108+
sys_deps,
109+
"SysDependencyManager",
110+
lambda gui: DummySysDepsManager(events, process),
111+
)
112+
monkeypatch.setattr("platform.system", lambda: "Linux")
113+
114+
gui = DummyGui(events)
115+
engine = DummyEngine()
116+
117+
assert engine.ensure_tools_installed(gui) is False
118+
assert events == [
119+
"system-install:patchelf",
120+
"python-install:/tmp/project/.venv:nuitka",
121+
]
122+
assert any("Timeout" in msg or "timeout" in msg for msg in gui.log)

0 commit comments

Comments
 (0)