Mutantenstufe der Plankritik nach der Pruefung vom 2026-09-28: Netzsperre per unshare, ungepufferte Ausgabe, Zeitlimit je Mutant 120 s, Aufraeumen bei SIGTERM, Modus-Zeile, mutanten_status, Faelle und Treiberfall 35

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Pascal MichelandClaude Opus 5.5 committed 2026-09-28 08:19:23 +02:00
1 parent bc9b4f4497
commit b1ad461acf
8 files changed
+336 -49

No files matched your search

+70 -28
View File
@@ -125,6 +125,16 @@ class RuecksetzenGescheitert(Exception):
super().__init__("Ruecksetzen gescheitert bei Mutant {0} ({1})".format(mutant_id, datei))
class SigtermUnterbrechung(Exception):
"""Wird aus dem SIGTERM-Handler ausgeloest, damit die finally-Bloecke
(Ruecksetzen der Zieldatei, Aufraeumen der Temp-Ordner) laufen. Siehe
Nachbesserung 2026-09-28, N4."""
def _sigterm_werfen(signum, frame):
raise SigtermUnterbrechung()
def ist_relativer_pfad(pfad):
if not isinstance(pfad, str) or pfad == "":
return False
@@ -442,45 +452,65 @@ def pytest_lauf(python_exe, knoten_liste, wurzel, home_dir, plugin_dir, zeitlimi
zeit_ueberschritten = False
absturz = None
proc = None
try:
proc = subprocess.Popen(
cmd, cwd=str(wurzel), env=env,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
start_new_session=True,
)
try:
proc.communicate(timeout=zeitlimit)
except subprocess.TimeoutExpired:
zeit_ueberschritten = True
try:
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
except (ProcessLookupError, PermissionError):
pass
proc.communicate()
except OSError as exc:
absturz = "pytest-Aufruf gescheitert ({0}): {1}".format(python_exe, exc)
proc = subprocess.Popen(
cmd, cwd=str(wurzel), env=env,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
start_new_session=True,
)
except OSError as exc:
absturz = "pytest-Aufruf gescheitert ({0}): {1}".format(python_exe, exc)
meldungen = []
try:
with open(ergebnis_pfad, "r", encoding="utf-8") as datei:
for zeile in datei:
zeile = zeile.strip()
if not zeile:
continue
if proc is not None:
try:
proc.communicate(timeout=zeitlimit)
except subprocess.TimeoutExpired:
zeit_ueberschritten = True
try:
meldungen.append(json.loads(zeile))
except ValueError:
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
except (ProcessLookupError, PermissionError):
pass
except OSError:
pass
proc.communicate()
except BaseException:
# Auch ein SIGTERM (SigtermUnterbrechung) darf keinen pytest-Prozess
# zurueck lassen: Prozessgruppe beenden, dann weiterwerfen, damit die
# finally-Bloecke der Aufrufer (Ruecksetzen der Zieldatei, Aufraeumen
# der Temp-Ordner) laufen. Siehe Nachbesserung 2026-09-28, N4.
try:
os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
except (ProcessLookupError, PermissionError):
pass
try:
proc.communicate()
except Exception:
pass
raise
meldungen = []
try:
with open(ergebnis_pfad, "r", encoding="utf-8") as datei:
for zeile in datei:
zeile = zeile.strip()
if not zeile:
continue
try:
meldungen.append(json.loads(zeile))
except ValueError:
pass
except OSError:
pass
return zeit_ueberschritten, meldungen, absturz
finally:
# Muss auch bei einem SIGTERM mitten im Lauf greifen (N4): sonst bleibt die
# Ergebnisdatei liegen, obwohl der pytest-Prozess schon beendet ist.
try:
os.remove(ergebnis_pfad)
except OSError:
pass
return zeit_ueberschritten, meldungen, absturz
def gruppen_ausfuehren(gruppen, wurzel, home_dir, plugin_dir, zeitlimit):
reports = {}
@@ -755,6 +785,7 @@ def ablauf(args, bericht):
plugin_dir = Path(tempfile.mkdtemp(prefix="mutanten_lauf_plugin_"))
home_dir = Path(tempfile.mkdtemp(prefix="mutanten_lauf_home_"))
aktueller_mutant_id = None
try:
(plugin_dir / (PLUGIN_NAME + ".py")).write_text(PLUGIN_QUELLTEXT, encoding="utf-8")
@@ -784,11 +815,13 @@ def ablauf(args, bericht):
print(" " + mutant["hinweis"])
continue
aktueller_mutant_id = mutant["id"]
status, test_ergebnisse = mutant_anwenden_und_pruefen(
mutant, muster["start"], muster["aus_zeilen"], muster["wird_zeilen"],
wurzel, home_dir, plugin_dir,
kontext["python_je_datei"], kontext["python"], args.zeitlimit,
)
aktueller_mutant_id = None
eintrag = {
"id": mutant["id"],
"datei": mutant["datei"],
@@ -808,6 +841,14 @@ def ablauf(args, bericht):
alle_ok = all(m["status"] == "OK" for m in bericht["mutanten"])
return 0 if alle_ok else 1
except SigtermUnterbrechung:
zeile = (
"ABBRUCH: SIGTERM, Mutant {0} zurueckgesetzt".format(aktueller_mutant_id)
if aktueller_mutant_id else "ABBRUCH: SIGTERM"
)
print(zeile)
bericht["befunde"] = list(befunde) + [zeile]
return 3
except RuecksetzenGescheitert as exc:
bericht["befunde"] = list(befunde) + [str(exc)]
return 2
@@ -830,6 +871,7 @@ def ausgabe_schreiben(bericht, args):
def main(argv=None):
signal.signal(signal.SIGTERM, _sigterm_werfen)
args = parse_args(argv)
bericht = {
"wurzel": args.wurzel,