safe_float/safe_int Hilfsfunktionen: doppelten try/except-Code konsolidiert

export_csv.py und export_sivas.py enthielten je ca. 10 nahezu identische
try/except (ValueError, TypeError)-Bloecke fuer "String zu float/int mit
Fallback". Neue gemeinsame Helfer safe_float()/safe_int()
(export_blockpatterns.py) ersetzen diese Bloecke; dabei auch zwei bislang
ungeschuetzte int()/float()-Aufrufe in export_sivas.py (Kreisel-Zaehler)
abgesichert, die bei fehlerhaften Attributwerten sonst abgestuerzt waeren.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 12:52:44 +02:00
parent 2842e1f1c9
commit b6b71c5997
3 changed files with 38 additions and 73 deletions
+16
View File
@@ -68,6 +68,22 @@ def load_patterns(cfg_path=None):
return patterns return patterns
def safe_float(value, default=0.0):
"""Wandelt value in float um; bei fehlendem/ungueltigem Wert Fallback default."""
try:
return float(value)
except (TypeError, ValueError):
return default
def safe_int(value, default=0):
"""Wandelt value in int um; bei fehlendem/ungueltigem Wert Fallback default."""
try:
return int(value)
except (TypeError, ValueError):
return default
def matches_any(bname, patterns): def matches_any(bname, patterns):
"""True, wenn bname (case-insensitiv) zu mindestens einem Muster passt.""" """True, wenn bname (case-insensitiv) zu mindestens einem Muster passt."""
upper = bname.upper() upper = bname.upper()
+7 -21
View File
@@ -41,7 +41,7 @@ import json
import sys import sys
import os import os
from export_blockpatterns import load_patterns, matches_any, csv_quote from export_blockpatterns import load_patterns, matches_any, csv_quote, safe_float
from export_planquadrat import load_planquadrat_config, resolve_origins, compute_planquadrat from export_planquadrat import load_planquadrat_config, resolve_origins, compute_planquadrat
from export_neighbors import ( from export_neighbors import (
load_neighbor_tolerance_mm, load_neighbor_tolerance_mm,
@@ -108,12 +108,9 @@ def get_drehung(block):
"""Liest DREHUNG aus Block-Attributen. Fallback: CAD-Rotation.""" """Liest DREHUNG aus Block-Attributen. Fallback: CAD-Rotation."""
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
drehung_raw = attribs.get("DREHUNG") drehung_raw = attribs.get("DREHUNG")
if drehung_raw is not None: if drehung_raw is None:
try: return block.get("rotation", 0.0)
return float(drehung_raw) return safe_float(drehung_raw, block.get("rotation", 0.0))
except (ValueError, TypeError):
pass
return block.get("rotation", 0.0)
def build_bogen_merkmale(block, eintrag): def build_bogen_merkmale(block, eintrag):
@@ -162,10 +159,7 @@ def get_laenge_mm(block):
"""Liest LAENGE (oder A) aus Block-Attributen. Fallback: 2000mm.""" """Liest LAENGE (oder A) aus Block-Attributen. Fallback: 2000mm."""
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
laenge = attribs.get("LAENGE") or attribs.get("A") laenge = attribs.get("LAENGE") or attribs.get("A")
try: return safe_float(laenge, 2000.0)
return float(laenge)
except (TypeError, ValueError):
return 2000.0
def build_gerade_merkmale(block): def build_gerade_merkmale(block):
@@ -228,16 +222,8 @@ def build_omni_sum_merkmale(cnt_boegen, cnt_wk, cnt_einzel, cnt_delta, cnt_doppe
def build_kreisel_merkmale(block): def build_kreisel_merkmale(block):
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
abstand_mm = attribs.get("ABSTAND", "2300") abstand_m = str(round(safe_float(attribs.get("ABSTAND", "2300"), 2300.0) / 1000.0, 2))
try: hoehe_m = str(round(safe_float(attribs.get("HOEHE", "0"), 0.0) / 1000.0, 2))
abstand_m = str(round(float(abstand_mm) / 1000.0, 2))
except (ValueError, TypeError):
abstand_m = "2.3"
hoehe_mm = attribs.get("HOEHE", "0")
try:
hoehe_m = str(round(float(hoehe_mm) / 1000.0, 2))
except (ValueError, TypeError):
hoehe_m = "0"
return { return {
"Abstand (Kreiselachse A - Kreiselachse) in Meter": abstand_m, "Abstand (Kreiselachse A - Kreiselachse) in Meter": abstand_m,
"Anzahl der Separatoren": attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")), "Anzahl der Separatoren": attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")),
+15 -52
View File
@@ -33,7 +33,7 @@ import json
import sys import sys
import os import os
from export_blockpatterns import load_patterns, matches_any, csv_quote from export_blockpatterns import load_patterns, matches_any, csv_quote, safe_float, safe_int
BLOCKPATTERNS = load_patterns() BLOCKPATTERNS = load_patterns()
@@ -99,10 +99,7 @@ def build_gefaellestrecke_details(block):
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
def mm_to_m(key): def mm_to_m(key):
try: return str(round(safe_float(attribs.get(key, "0")) / 1000.0, 2))
return str(round(float(attribs.get(key, "0")) / 1000.0, 2))
except (ValueError, TypeError):
return "0"
return { return {
"Hoehe oben": mm_to_m("HOEHE_VON_mm"), "Hoehe oben": mm_to_m("HOEHE_VON_mm"),
@@ -177,10 +174,7 @@ def build_variofoerderer_details(block):
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
def to_int(key): def to_int(key):
try: return safe_int(attribs.get(key, "0") or "0")
return int(attribs.get(key, "0") or "0")
except (ValueError, TypeError):
return 0
return { return {
"Anzahl Foerdergruppen": to_int("ANZAHL_VF"), "Anzahl Foerdergruppen": to_int("ANZAHL_VF"),
@@ -218,16 +212,8 @@ def build_variofoerderer_details(block):
def build_kreisel_details(block): def build_kreisel_details(block):
"""Merkmale-Dict fuer einen ILS Kreisel.""" """Merkmale-Dict fuer einen ILS Kreisel."""
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
abstand_mm = attribs.get("ABSTAND", "2300") abstand_m = str(round(safe_float(attribs.get("ABSTAND", "2300"), 2300.0) / 1000.0, 2))
try: hoehe_m = str(round(safe_float(attribs.get("HOEHE", "0"), 0.0) / 1000.0, 2))
abstand_m = str(round(float(abstand_mm) / 1000.0, 2))
except (ValueError, TypeError):
abstand_m = "2.3"
hoehe_mm = attribs.get("HOEHE", "0")
try:
hoehe_m = str(round(float(hoehe_mm) / 1000.0, 2))
except (ValueError, TypeError):
hoehe_m = "0"
return { return {
"Hoehe in m": hoehe_m, "Hoehe in m": hoehe_m,
"Kreiselart": attribs.get("KREISELART", "STANDARD"), "Kreiselart": attribs.get("KREISELART", "STANDARD"),
@@ -353,13 +339,8 @@ def process_blocks(blocks, lookup):
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
artinr = attribs.get("ARTINR", "") artinr = attribs.get("ARTINR", "")
key = artinr if artinr else "AP110" key = artinr if artinr else "AP110"
laenge = 2000.0
laenge_attr = attribs.get("LAENGE") or attribs.get("A") laenge_attr = attribs.get("LAENGE") or attribs.get("A")
if laenge_attr: laenge = safe_float(laenge_attr, 2000.0)
try:
laenge = float(laenge_attr)
except (ValueError, TypeError):
pass
if key not in gerade_groups: if key not in gerade_groups:
gerade_groups[key] = { gerade_groups[key] = {
"anzahl": 0, "laenge_mm": 0.0, "anzahl": 0, "laenge_mm": 0.0,
@@ -384,17 +365,17 @@ def process_blocks(blocks, lookup):
kreisel_list.append(block) kreisel_list.append(block)
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
counters["anzahl_kreisel"] += 1 counters["anzahl_kreisel"] += 1
n_sep = int(attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")) or "2") n_sep = safe_int(attribs.get("ANZAHL_SEPARATOR", attribs.get("N_SEPARATOREN", "2")) or "2", 2)
kreiselart = attribs.get("KREISELART", "STANDARD") kreiselart = attribs.get("KREISELART", "STANDARD")
if kreiselart == "PIN": if kreiselart == "PIN":
counters["anzahl_kreisel_pinband"] += 1 counters["anzahl_kreisel_pinband"] += 1
counters["sep_kreisel_pin"] += n_sep counters["sep_kreisel_pin"] += n_sep
else: else:
counters["sep_kreisel_ohne_pin"] += n_sep counters["sep_kreisel_ohne_pin"] += n_sep
abstand_mm = float(attribs.get("ABSTAND", "2300") or "2300") abstand_mm = safe_float(attribs.get("ABSTAND", "2300") or "2300", 2300.0)
counters["laengen_kreisel"] += abstand_mm / 1000.0 counters["laengen_kreisel"] += abstand_mm / 1000.0
counters["anzahl_separatoren"] += n_sep counters["anzahl_separatoren"] += n_sep
counters["anzahl_scanner"] += int(attribs.get("ANZAHL_SCANNER", attribs.get("N_SCANNER", "0")) or "0") counters["anzahl_scanner"] += safe_int(attribs.get("ANZAHL_SCANNER", attribs.get("N_SCANNER", "0")) or "0")
if counters["anzahl_streckengruppen"] == 0: if counters["anzahl_streckengruppen"] == 0:
counters["anzahl_streckengruppen"] = 1 counters["anzahl_streckengruppen"] = 1
continue continue
@@ -406,18 +387,9 @@ def process_blocks(blocks, lookup):
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
counters["anzahl_variofoerderer"] += 1 counters["anzahl_variofoerderer"] += 1
vario_list.append(block) vario_list.append(block)
try: counters["laengen_variofoerderer"] += safe_float(attribs.get("L_VF_m", "0") or "0")
counters["laengen_variofoerderer"] += float(attribs.get("L_VF_m", "0") or "0") counters["anzahl_separatoren"] += safe_int(attribs.get("ANZAHL_SEPARATOR", "0") or "0")
except (ValueError, TypeError): counters["anzahl_scanner"] += safe_int(attribs.get("ANZAHL_SCANNER", "0") or "0")
pass
try:
counters["anzahl_separatoren"] += int(attribs.get("ANZAHL_SEPARATOR", "0") or "0")
except (ValueError, TypeError):
pass
try:
counters["anzahl_scanner"] += int(attribs.get("ANZAHL_SCANNER", "0") or "0")
except (ValueError, TypeError):
pass
if counters["anzahl_streckengruppen"] == 0: if counters["anzahl_streckengruppen"] == 0:
counters["anzahl_streckengruppen"] = 1 counters["anzahl_streckengruppen"] = 1
continue continue
@@ -428,18 +400,9 @@ def process_blocks(blocks, lookup):
attribs = block.get("attribs", {}) attribs = block.get("attribs", {})
counters["anzahl_gefaellestrecken"] += 1 counters["anzahl_gefaellestrecken"] += 1
gefaelle_list.append(block) gefaelle_list.append(block)
try: counters["laengen_gefaellestrecken"] += safe_float(attribs.get("L_GF_m", "0") or "0")
counters["laengen_gefaellestrecken"] += float(attribs.get("L_GF_m", "0") or "0") counters["anzahl_separatoren"] += safe_int(attribs.get("ANZAHL_SEPARATOR", "0") or "0")
except (ValueError, TypeError): counters["anzahl_scanner"] += safe_int(attribs.get("ANZAHL_SCANNER", "0") or "0")
pass
try:
counters["anzahl_separatoren"] += int(attribs.get("ANZAHL_SEPARATOR", "0") or "0")
except (ValueError, TypeError):
pass
try:
counters["anzahl_scanner"] += int(attribs.get("ANZAHL_SCANNER", "0") or "0")
except (ValueError, TypeError):
pass
if counters["anzahl_streckengruppen"] == 0: if counters["anzahl_streckengruppen"] == 0:
counters["anzahl_streckengruppen"] = 1 counters["anzahl_streckengruppen"] = 1
continue continue