CSV-Escaping-Bug behoben, export.cfg-Mehrfachparsing beseitigt
format_csv_line() in export_csv.py/export_sivas.py hat Feldwerte per f-String direkt in doppelte Anfuehrungszeichen gesetzt, ohne ein eingebettetes '"' zu escapen (z.B. eine Bezeichnung mit Anfuehrungszeichen haette die Spaltengrenze der Zeile kaputt gemacht). Neuer gemeinsamer Helper csv_quote() (export_blockpatterns.py) verdoppelt eingebettete Anfuehrungszeichen nach RFC 4180. Ausserdem: export.cfg wurde bisher von vier unabhaengigen load_*-Funktionen (load_patterns, load_neighbor_tolerance_mm, load_omniflo_cell_size_mm, load_planquadrat_config) separat von der Platte gelesen und geparst. Neuer gemeinsamer, gecachter load_export_cfg() in export_blockpatterns.py sorgt dafuer, dass export.cfg pro Exportlauf nur noch einmal gelesen wird. Dabei auch die in export_csv.py bereits genutzte, aber in export_planquadrat.py fehlende resolve_origins()-Funktion (automatische Planquadrat-Ursprungs- Ermittlung) ergaenzt - ohne sie war der Import von export_csv.py gebrochen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ inhaltlich Teilmengen der groben Lisp-Kategorien sind.
|
||||
|
||||
import configparser
|
||||
import fnmatch
|
||||
import functools
|
||||
import os
|
||||
|
||||
|
||||
@@ -32,6 +33,25 @@ def cfg_path_from_env(default_dir=None):
|
||||
return os.path.join(base, "cfg", "export.cfg")
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def load_export_cfg(cfg_path):
|
||||
"""Liest und parst export.cfg genau einmal je Pfad (Prozess-Cache).
|
||||
|
||||
Gemeinsam genutzt von export_blockpatterns/export_neighbors/
|
||||
export_planquadrat, damit export.cfg pro Exportlauf nur einmal von der
|
||||
Platte gelesen/geparst wird statt bei jedem der bisher unabhaengigen
|
||||
load_*-Aufrufe erneut. inline_comment_prefixes=("#",) ist noetig, da
|
||||
export.cfg Werte wie "numerisch # zahlen, von links nach rechts" mit
|
||||
Inline-Kommentaren nutzt (siehe [Planquadrate_*]-Sektionen).
|
||||
cfg_path muss bereits aufgeloest sein (siehe cfg_path_from_env) - der
|
||||
Cache ist sonst nicht treffsicher (ein "None"-Schluessel wuerde nie den
|
||||
tatsaechlichen Pfad abbilden).
|
||||
"""
|
||||
parser = configparser.ConfigParser(inline_comment_prefixes=("#",))
|
||||
parser.read(cfg_path, encoding="utf-8")
|
||||
return parser
|
||||
|
||||
|
||||
def load_patterns(cfg_path=None):
|
||||
"""Liest [blockpattern] aus export.cfg.
|
||||
|
||||
@@ -40,8 +60,7 @@ def load_patterns(cfg_path=None):
|
||||
"""
|
||||
if cfg_path is None:
|
||||
cfg_path = cfg_path_from_env()
|
||||
parser = configparser.ConfigParser()
|
||||
parser.read(cfg_path, encoding="utf-8")
|
||||
parser = load_export_cfg(cfg_path)
|
||||
patterns = {}
|
||||
if parser.has_section("blockpattern"):
|
||||
for key, value in parser.items("blockpattern"):
|
||||
@@ -53,3 +72,12 @@ def matches_any(bname, patterns):
|
||||
"""True, wenn bname (case-insensitiv) zu mindestens einem Muster passt."""
|
||||
upper = bname.upper()
|
||||
return any(fnmatch.fnmatchcase(upper, p.upper()) for p in patterns)
|
||||
|
||||
|
||||
def csv_quote(value):
|
||||
"""Escapt einen Wert fuer ein doppelt-angefuehrtes, semikolon-getrenntes CSV-Feld.
|
||||
|
||||
Verdoppelt eingebettete Anfuehrungszeichen (RFC 4180), damit z.B. eine
|
||||
Bezeichnung mit einem '"' das Feldende nicht vortaeuscht.
|
||||
"""
|
||||
return str(value).replace('"', '""')
|
||||
|
||||
Reference in New Issue
Block a user