Skip to content

Commit 44eeeba

Browse files
committed
compiler: durcissement express des flux stdout/stderr et test de garde
- Evite les crashs quand stdout/stderr sont indisponibles sur le processus de compilation. - Ajoute un warning explicite et stop proprement la lecture temps reel. - Ajoute un test de non-regression pour ce cas edge.
1 parent 6b33f4e commit 44eeeba

2 files changed

Lines changed: 49 additions & 12 deletions

File tree

Core/Compiler/compiler.py

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,10 @@
2929
from __future__ import annotations
3030

3131
import os
32-
import sys
3332
import subprocess
3433
import select
3534
import time
36-
from pathlib import Path
37-
from typing import Optional, Dict, Any, List, Callable
35+
from typing import Optional, Dict, List
3836
from datetime import datetime
3937
from enum import Enum
4038

@@ -156,20 +154,30 @@ def _read_output(self) -> None:
156154
if self.process is None or self.process.poll() is not None:
157155
break
158156

157+
stdout_stream = self.process.stdout
158+
stderr_stream = self.process.stderr
159+
if stdout_stream is None and stderr_stream is None:
160+
self.error_ready.emit(
161+
"Warning: process streams are unavailable; stopping realtime read."
162+
)
163+
break
164+
165+
watched_streams = [s for s in (stdout_stream, stderr_stream) if s is not None]
166+
if not watched_streams:
167+
break
168+
159169
# Utiliser select pour attendre des données
160170
try:
161-
ready, _, _ = select.select(
162-
[self.process.stdout, self.process.stderr], [], [], 0.1
163-
)
171+
ready, _, _ = select.select(watched_streams, [], [], 0.1)
164172

165173
for stream in ready:
166-
if stream == self.process.stdout:
167-
line = self.process.stdout.readline()
174+
if stdout_stream is not None and stream == stdout_stream:
175+
line = stdout_stream.readline()
168176
if line:
169177
self.output_ready.emit(line.rstrip())
170178
self._update_progress(line)
171-
elif stream == self.process.stderr:
172-
line = self.process.stderr.readline()
179+
elif stderr_stream is not None and stream == stderr_stream:
180+
line = stderr_stream.readline()
173181
if line:
174182
self.error_ready.emit(line.rstrip())
175183
self._update_progress(line)
@@ -186,7 +194,7 @@ def _read_remaining(self) -> None:
186194

187195
# Lire stdout restant
188196
try:
189-
remaining_stdout = self.process.stdout.read()
197+
remaining_stdout = self.process.stdout.read() if self.process.stdout else ""
190198
if remaining_stdout:
191199
for line in remaining_stdout.strip().split("\n"):
192200
if line:
@@ -196,7 +204,7 @@ def _read_remaining(self) -> None:
196204

197205
# Lire stderr restant
198206
try:
199-
remaining_stderr = self.process.stderr.read()
207+
remaining_stderr = self.process.stderr.read() if self.process.stderr else ""
200208
if remaining_stderr:
201209
for line in remaining_stderr.strip().split("\n"):
202210
if line:
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# Copyright 2026 Ague Samuel Amen
3+
4+
from __future__ import annotations
5+
6+
import pytest
7+
8+
pytest.importorskip("PySide6")
9+
10+
from Core.Compiler.compiler import CompilationThread
11+
12+
13+
class _FakeProcessNoStreams:
14+
stdout = None
15+
stderr = None
16+
17+
def poll(self):
18+
return None
19+
20+
21+
def test_read_output_handles_missing_streams_without_crash() -> None:
22+
t = CompilationThread(program="python", args=[])
23+
t.process = _FakeProcessNoStreams()
24+
captured_errors: list[str] = []
25+
t.error_ready.connect(lambda msg: captured_errors.append(msg))
26+
27+
t._read_output()
28+
29+
assert any("streams are unavailable" in m for m in captured_errors)

0 commit comments

Comments
 (0)