/**
* 🩺 PYTHON TRACEBACK & FEHLER-DOLMETSCHER 🩺
* ============================================
* Basiert auf der didaktischen Fehlerforschung (Brett Becker et al., SIGCSE).
* Übersetzt kryptische Python-Fehlermeldungen in einfühlsames, klares Deutsch
* mit konkreten Handlungsanweisungen für Schülerinnen und Schüler.
*/
(function () {
const ERROR_RULES = [
{
type: "SyntaxError",
pattern: /SyntaxError: (invalid syntax|expected ':'|was never closed|cannot assign to expression)/i,
title: "✍️ Schreibweise- & Grammatikfehler (SyntaxError)",
explanation: "Python versteht den Aufbau deines Codes an dieser Stelle nicht. Meist fehlt ein Satzzeichen oder eine Klammer.",
causes: [
"Fehlender Doppelpunkt : am Ende von def, if, elif, else, for oder while.",
"Eine geöffnete Klammer ( oder ein Anführungszeichen \" wurde nicht geschlossen.",
"Verwechslung von Zuweisung = und Vergleich == in einer if-Bedingung."
],
fix: "Prüfe die Zeile ganz genau: Steht am Zeilenende ein Doppelpunkt? Sind alle Klammern und Anführungszeichen paarweise geschlossen? Nutze if a == b: zum Vergleichen!"
},
{
type: "IndentationError",
pattern: /IndentationError: (expected an indented block|unindent does not match)/i,
title: "📐 Einrückungsfehler (IndentationError)",
explanation: "Python nutzt Einrückungen (Standard: genau 4 Leerzeichen), um zu erkennen, welcher Code zu einer Funktion, `if`-Bedingung oder Schleife gehört.",
causes: [
"Nach einem Doppelpunkt (def ...:, if ...:, for ...:) fehlt die Einrückung.",
"Leerzeichen und Tabulatoren wurden versehentlich gemischt."
],
fix: "Drücke nach dem Doppelpunkt Enter und rücke die Zeile mit Tab oder 4 Leerzeichen ein. Mit Umschalt + Tab kannst du Zeilen wieder nach links rücken."
},
{
type: "TypeError String Concatenation",
pattern: /TypeError: can only concatenate str \(not "(int|float)"\) to str/i,
title: "🔤 Text und Zahl vermischt (TypeError)",
explanation: "Du versuchst einen Text (str) und eine Zahl (int/float) mit + zusammenzufügen.",
causes: [
"In Python kann man Text und Zahlen nicht einfach addieren (z.B. \"Alter: \" + 16 schlägt fehl)."
],
fix: "Nutze einen modernen f-String: f\"Alter: {alter}\" oder wandle die Zahl mit str(zahl) um."
},
{
type: "TypeError NoneType",
pattern: /TypeError: 'NoneType' object is not (subscriptable|iterable|callable)/i,
title: "🕳️ Zugriff auf 'None' (TypeError)",
explanation: "Du versuchst auf ein Objekt zuzugreifen, das gar nicht existiert oder den Wert None hat.",
causes: [
"Eine aufgerufene Funktion hat vergessen, ihr Ergebnis mit return zurückzugeben.",
"Eine Listen-Methode wie liste.sort() oder liste.append() gibt immer None zurück (sie verändert die Liste direkt!)."
],
fix: "Prüfe in deinen Funktionen, ob am Ende ein return ergebnis steht. Schreibe niemals x = liste.sort(), sondern nur liste.sort()."
},
{
type: "AttributeError",
pattern: /AttributeError: '([^']+)' object has no attribute '([^']+)'/i,
title: "🔍 Unbekanntes Attribut oder Methode (AttributeError)",
explanation: "Python konnte an dem Objekt die gewünschte Methode oder Eigenschaft nicht finden.",
causes: [
"Tippfehler im Namen der Methode oder Variable (z.B. anhängen statt append).",
"Groß- und Kleinschreibung nicht beachtet (z.B. .Name statt .name).",
"In der Klassen-Methode wurde self. vergessen (z.B. alter statt self.alter)."
],
fix: "Überprüfe die Schreibweise des Attributs exakt Buchstabe für Buchstabe. Nutze in Klassen immer self.attributname."
},
{
type: "TypeError self missing",
pattern: /TypeError: ([^\(]+)\(\) takes \d+ positional arguments? but \d+ were given/i,
title: "🧩 Fehlendes 'self' in Methoden-Definition (TypeError)",
explanation: "Eine Methode in einer Klasse erwartet weniger oder mehr Parameter als übergeben wurden.",
causes: [
"Der klassische Anfängerfehler: Du hast im Methodenkopf das self als ersten Parameter vergessen! (z.B. def fahren(): statt def fahren(self):)."
],
fix: "Schreibe in jeder Methode deiner Klasse als allerersten Parameter immer self: z.B. def beschleunigen(self, kmh):."
},
{
type: "IndexError",
pattern: /IndexError: (list index out of range|string index out of range)/i,
title: "🎯 Index außerhalb des gültigen Bereichs (IndexError)",
explanation: "Du greifst auf eine Position (Index) in einer Liste oder einem String zu, die gar nicht existiert.",
causes: [
"Off-by-One Fehler: In Python beginnt die Zählung bei 0! Eine Liste mit 3 Elementen hat nur die Indizes 0, 1, 2 (nicht 3).",
"Die Liste ist noch leer."
],
fix: "Prüfe die Länge der Liste mit len(liste). Das letzte Element erreichst du immer sicher mit liste[-1]."
},
{
type: "KeyError",
pattern: /KeyError: (.+)/i,
title: "🔑 Schlüssel nicht im Dictionary gefunden (KeyError)",
explanation: "Du hast versucht, aus einem Dictionary dict[schluessel] einen Eintrag abzufragen, der nicht existiert.",
causes: [
"Der gesuchte Key ist nicht im Dictionary vorhanden oder falsch geschrieben."
],
fix: "Nutze die sichere Abfrage mit dict.get(key, standardwert) statt der eckigen Klammern, um Abstürze zu vermeiden!"
},
{
type: "NameError",
pattern: /NameError: name '([^']+)' is not defined/i,
title: "❓ Name nicht definiert (NameError)",
explanation: "Python kennt das Wort oder die Variable an dieser Stelle nicht.",
causes: [
"Die Variable wurde weiter unten oder in einer anderen Funktion definiert (Gültigkeitsbereich/Scope).",
"Ein Tippfehler im Variablennamen.",
"Ein Modul wie import math wurde noch nicht importiert."
],
fix: "Prüfe, ob du die Variable vor ihrer Benutzung angelegt hast und ob alle benötigten Module oben mit import ... eingebunden sind."
},
{
type: "ValueError",
pattern: /ValueError: (invalid literal for int\(\)|math domain error)/i,
title: "⚠️ Ungültiger Wert (ValueError)",
explanation: "Der Datentyp war zwar richtig, aber der übergebene Wert macht für diesen Befehl keinen Sinn.",
causes: [
"Umwandlung von Text mit Buchstaben in eine Zahl (z.B. int(\"hallo\")).",
"Mathematisch unzulässige Werte (z.B. Wurzel aus einer negativen Zahl)."
],
fix: "Prüfe vorher mit text.isdigit(), ob der Text nur aus Ziffern besteht, oder sichere die Umwandlung mit try-except ValueError: ab."
},
{
type: "AssertionError",
pattern: /AssertionError: (.+)/i,
title: "🧪 Unittest fehlgeschlagen (AssertionError)",
explanation: "Dein Programmcode läuft zwar ohne Absturz durch, liefert aber noch nicht das von der Aufgabe erwartete Ergebnis.",
causes: [
"Ein Rechenfehler oder eine falsche Formel in der Funktion.",
"Rückgabewert vergessen (return fehlt).",
"Grenzfall nicht beachtet (z.B. 0 oder leere Liste)."
],
fix: "Vergleiche die Rückgabe deiner Funktion mit dem in der Testmeldung geforderten Soll-Wert. Lies die Aufgabenbeschreibung noch einmal aufmerksam durch!"
},
{
type: "ZeroDivisionError",
pattern: /ZeroDivisionError: division by zero/i,
title: "➗ Division durch Null (ZeroDivisionError)",
explanation: "In der Mathematik und in Python ist das Teilen durch 0 nicht erlaubt.",
causes: [
"Der Nenner / Teiler hat den Wert 0."
],
fix: "Sichere die Division mit einer if teiler != 0: Bedingung oder fange sie mit try-except ZeroDivisionError: ab."
}
];
window.interpretiereTraceback = function (errorText) {
if (!errorText || !errorText.trim()) {
return {
matched: false,
name: "💡 Bitte füge eine Fehlermeldung ein",
title: "💡 Bitte füge eine Fehlermeldung ein",
explanation: "Kopiere deinen Traceback aus dem Terminal und füge ihn hier ein.",
causes: ["Noch keine Fehlermeldung eingegeben."],
fixes: ["Starte deinen Code oder Unittests, um eventuelle Fehler zu sehen."],
fix: "Starte deinen Code oder Unittests, um eventuelle Fehler zu sehen."
};
}
for (let rule of ERROR_RULES) {
if (rule.pattern.test(errorText)) {
return {
matched: true,
type: rule.type,
name: rule.title,
title: rule.title,
explanation: rule.explanation,
causes: rule.causes,
fixes: [rule.fix],
fix: rule.fix
};
}
}
return {
matched: false,
name: "🔍 Allgemeiner Python-Fehler",
title: "🔍 Allgemeiner Python-Fehler",
explanation: "Lies den Traceback immer von ganz unten nach oben: In der allerletzten Zeile nennt Python die genaue Fehlerart und Zeilennummer.",
causes: ["Syntax- oder Logikfehler in deinem Code."],
fixes: ["Überprüfe die in der letzten Zeile genannte Zeilennummer in deiner aufgabe.py Datei."],
fix: "Überprüfe die in der letzten Zeile genannte Zeilennummer in deiner aufgabe.py Datei."
};
};
// Exportiertes Objekt für Workspace & Widgets
window.INTERPRETER = {
translate: window.interpretiereTraceback
};
// Widget rendern, falls Container vorhanden
document.addEventListener("DOMContentLoaded", () => {
const interpreterEl = document.getElementById("error-interpreter-widget");
if (!interpreterEl) return;
const input = document.getElementById("error-input");
const btn = document.getElementById("btn-interpret-error");
const result = document.getElementById("error-result");
if (btn && input && result) {
btn.addEventListener("click", () => {
const res = window.interpretiereTraceback(input.value);
document.getElementById("error-name").innerText = res.title;
document.getElementById("error-explanation").innerHTML = res.explanation;
const fixList = document.getElementById("error-fixes");
if (fixList) {
fixList.innerHTML = (res.causes || []).map(c => `