- systeme/macos.py : SystemeMacOS (sons afplay, modificateurs Quartz, chemins, verrou d'instance fcntl) implementant le contrat Systeme. - systeme/__init__.py : choisir_systeme() fabrique selon sys.platform, refuse clairement une plateforme inconnue. - core.py : verrou d'instance (fcntl, LOCK_PATH, acquire_single_instance_lock) retire ; import os retire aussi, verifie inutilise ailleurs dans le fichier. - app.py : passe par choisir_systeme().acquerir_verrou_instance(dossier_donnees() + instance.lock) au lieu de core.acquire_single_instance_lock. Seul changement dans ce fichier, nom de fichier inchange. - tests/test_single_instance.py : reoriente vers choisir_systeme(), memes assertions et meme nombre de tests. - tests/test_systeme.py (nouveau) : verrou du test_le_verrou_refuse_une_seconde_prise garde desormais la reference au premier verrou — sans variable, CPython le garbage-collectait entre les deux assert (refcount 0 -> fermeture -> verrou relache), le test passait pour la mauvaise raison. 89 tests verts (84 + 5).
52 lines
1.7 KiB
Python
52 lines
1.7 KiB
Python
"""Garde d'instance unique.
|
|
|
|
Contexte (bug du 2026-08-21) : deux bundles Zonza portant le MÊME identifiant
|
|
(`/Applications/Zonza.app` et un fantôme resté dans la Corbeille, encore
|
|
enregistré auprès de LaunchServices) pouvaient tourner simultanément. Chaque
|
|
instance chargeait son propre modèle MLX-Whisper `medium` et captait la même
|
|
hotkey globale : un seul appui déclenchait deux transcriptions, la machine
|
|
saturait.
|
|
|
|
Purger le fantôme corrige l'incident du jour ; cette garde corrige la CLASSE de
|
|
bug — quel que soit le nombre de bundles présents, la 2e instance renonce.
|
|
"""
|
|
import os
|
|
import tempfile
|
|
|
|
from systeme import choisir_systeme
|
|
|
|
|
|
def _tmp_lock():
|
|
return os.path.join(tempfile.mkdtemp(), "sous-dossier", "zonza.lock")
|
|
|
|
|
|
def test_premiere_instance_obtient_le_verrou():
|
|
handle = choisir_systeme().acquerir_verrou_instance(_tmp_lock())
|
|
assert handle is not None
|
|
|
|
|
|
def test_deuxieme_instance_est_refusee():
|
|
path = _tmp_lock()
|
|
premier = choisir_systeme().acquerir_verrou_instance(path)
|
|
assert premier is not None
|
|
assert choisir_systeme().acquerir_verrou_instance(path) is None
|
|
|
|
|
|
def test_verrou_repris_apres_liberation():
|
|
path = _tmp_lock()
|
|
premier = choisir_systeme().acquerir_verrou_instance(path)
|
|
premier.close()
|
|
assert choisir_systeme().acquerir_verrou_instance(path) is not None
|
|
|
|
|
|
def test_cree_le_dossier_parent_manquant():
|
|
path = _tmp_lock()
|
|
assert not os.path.isdir(os.path.dirname(path))
|
|
choisir_systeme().acquerir_verrou_instance(path)
|
|
assert os.path.isdir(os.path.dirname(path))
|
|
|
|
|
|
def test_deux_chemins_differents_ne_se_genent_pas():
|
|
assert choisir_systeme().acquerir_verrou_instance(_tmp_lock()) is not None
|
|
assert choisir_systeme().acquerir_verrou_instance(_tmp_lock()) is not None
|