feat(gui): échec amont clair si dossier de sortie non inscriptible (P1-6)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-29 19:33:16 +02:00
parent 3a981eb15a
commit ee1f86d55e
2 changed files with 34 additions and 0 deletions

View File

@@ -99,6 +99,10 @@ def discover_documents(input_path, extensions: Optional[Sequence[str]] = None) -
return []
class OutputNotWritableError(RuntimeError):
"""Le dossier de sortie n'est pas inscriptible (échec amont, message clair)."""
@dataclass
class DocResult:
"""Détail anonymisé d'un document traité (pour la télémétrie d'usage).
@@ -195,6 +199,19 @@ class ProcessingRunner:
log("Aucun document supporté détecté.")
return summary
# Sonde amont : on vérifie une seule fois que le dossier de sortie est
# inscriptible AVANT la boucle, pour un échec clair et unique (P1-6)
# plutôt qu'une erreur cryptique répétée à chaque document.
try:
out_root.mkdir(parents=True, exist_ok=True)
probe = out_root / ".anon_write_test"
probe.write_text("", encoding="utf-8")
probe.unlink()
except Exception as exc:
raise OutputNotWritableError(
f"Dossier de sortie non inscriptible : {out_root} ({exc})"
) from exc
for index, doc in enumerate(docs, start=1):
if stop_event is not None and stop_event.is_set():
summary.stopped = True

View File

@@ -197,6 +197,23 @@ def test_progress_callbacks(tmp_path):
assert (2, 2) in events # progression finale atteinte
def test_run_fails_fast_when_output_not_writable(tmp_path, monkeypatch):
from gui_v6.processing_runner import ProcessingRunner, OutputNotWritableError
src = tmp_path / "in"
src.mkdir()
(src / "a.txt").write_text("x", encoding="utf-8")
out = tmp_path / "ro"
out.mkdir()
def boom(*a, **k):
raise PermissionError("read-only")
monkeypatch.setattr("gui_v6.processing_runner.Path.mkdir", boom)
runner = ProcessingRunner(process_fn=lambda d, o: {})
with pytest.raises(OutputNotWritableError):
runner.run(src, out)
def test_no_double_run(tmp_path):
_touch(tmp_path / "a.pdf")
started = threading.Event()