[FIX] Python-Interpreter fuer Export robust ermitteln + Sprachtexte

Unter Windows 11 traf ein blankes "python" den WindowsApps-Platzhalter
(kein echter Interpreter, Exitcode 49). Der Export schrieb die JSON, aber
nie eine CSV - ohne jede Fehlermeldung, da "fire and forget" per startapp.

- export:python-exe: Aufloesungsreihenfolge cfg/export.cfg [python]
  interpreter= -> DXFM_PYTHON -> Projekt-venv -> %SystemRoot%/py.exe ->
  "python"; WindowsApps-Platzhalter werden verworfen
- export:run-python: synchroner Aufruf per WScript.Shell.Run, stdout/stderr
  nach DXFM_LOG/export_python.log, Exitcode-Auswertung
- export:log-ausgeben: erste Logzeilen in die BricsCAD-Kommandozeile
- setenv.bat setzt DXFM_PYTHON (venv oder py-Launcher)
- cfg/export.cfg: neue Sektion [python]
- lang/de_DE.json, lang/en_GB.json: neue Meldungstexte (exp-python-exe,
  exp-export-done, exp-python-error, exp-python-store-hint, exp-csv-missing,
  exp-python-call-failed)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-29 12:05:54 +02:00
parent f2b72df6e0
commit 88fc00a5c0
5 changed files with 180 additions and 7 deletions
+140 -7
View File
@@ -68,6 +68,115 @@
) )
;; ============================================================
;; PYTHON-INTERPRETER ERMITTELN
;; ============================================================
;; BricsCAD erbt seine Umgebung von start_briscad.bat/setenv.bat - dort wird
;; das Projekt-venv NICHT aktiviert. Ein blankes "python" trifft unter Windows
;; 11 daher den WindowsApps-Platzhalter
;; (%LOCALAPPDATA%\Microsoft\WindowsApps\python.exe), der kein Interpreter ist,
;; sondern nur auf den Microsoft Store verweist und mit Exitcode 49 abbricht.
;; Der Export schrieb dann zwar die JSON, aber nie eine CSV.
;;
;; Aufloesungsreihenfolge (erster Treffer gewinnt):
;; 1. cfg/export.cfg [python] interpreter= (explizite Vorgabe)
;; 2. Umgebungsvariable DXFM_PYTHON (von setenv.bat gesetzt)
;; 3. <DXFMAKRO>/.venv/Scripts/python.exe (Projekt-venv, siehe install_py.bat)
;; 4. "py" (Python-Launcher, C:\Windows\py.exe) - kein Store-Platzhalter
;; 5. "python" (PATH) als letzter Ausweg
(defun export:python-store-stub-p (pfad / p)
(setq p (strcase (vl-string-translate "\\" "/" pfad)))
(and (vl-string-search "/WINDOWSAPPS/" p) T)
)
;; --- Kandidat pruefen: existierende Datei und kein Store-Platzhalter ---
(defun export:python-kandidat-ok-p (pfad)
(and pfad
(/= pfad "")
(findfile pfad)
(not (export:python-store-stub-p pfad)))
)
(defun export:python-exe ( / cfg-wert env-wert venv-pfad launcher-pfad)
(cond
;; 1. Explizite Vorgabe aus der Config
((export:python-kandidat-ok-p
(setq cfg-wert (export:cfg "python" "interpreter" "")))
cfg-wert)
;; 2. Umgebungsvariable
((export:python-kandidat-ok-p (setq env-wert (getenv "DXFM_PYTHON")))
env-wert)
;; 3. Projekt-venv
((export:python-kandidat-ok-p
(setq venv-pfad (strcat (cond ((getenv "DXFMAKRO")) ("."))
"/.venv/Scripts/python.exe")))
venv-pfad)
;; 4. Python-Launcher (liegt in C:\Windows, daher kein Store-Platzhalter).
;; Voller Pfad statt nur "py.exe": findfile durchsucht die BricsCAD-
;; Suchpfade, nicht zwangslaeufig den System-PATH.
((export:python-kandidat-ok-p
(setq launcher-pfad (strcat (cond ((getenv "SystemRoot")) ("C:/Windows"))
"/py.exe")))
launcher-pfad)
;; 5. Letzter Ausweg - kann der Store-Platzhalter sein, deshalb wird der
;; Exitcode nach dem Aufruf geprueft (siehe export:run-python).
(t "python")
)
)
;; --- Python-Skript synchron ausfuehren, Ausgabe in Logdatei ---
;; Ersetzt das frueherer "fire and forget" (startapp "cmd" "/c ..."): das
;; cmd-Fenster schloss sich sofort wieder, jede Python-Fehlermeldung (fehlender
;; Interpreter, Traceback) war unsichtbar und der Befehl meldete trotzdem
;; "Export gestartet". Jetzt wird ueber WScript.Shell.Run mit bWaitOnReturn=T
;; gewartet, stdout/stderr nach DXFM_LOG/export_python.log umgeleitet und der
;; Exitcode ausgewertet.
;; Rueckgabe: Exitcode (0 = ok) oder nil, wenn der Aufruf selbst scheiterte.
(defun export:run-python (py-exe py-skript args log-pfad / cmd sh rc code)
;; Aeussere Anfuehrungszeichen um die gesamte /c-Zeile: sonst verschluckt
;; cmd.exe die Quotes um den Interpreterpfad (Leerzeichen in "Program Files").
(setq cmd (strcat "cmd.exe /c \"\"" py-exe "\""))
(setq cmd (strcat cmd " \"" py-skript "\""))
(foreach a args (setq cmd (strcat cmd " \"" a "\"")))
(setq cmd (strcat cmd " > \"" log-pfad "\" 2>&1\""))
;; Run(cmd, 0, T): 0 = kein Fenster, T = warten und Exitcode liefern.
(setq rc
(vl-catch-all-apply
'(lambda ()
(setq sh (vlax-create-object "WScript.Shell"))
(setq code (vlax-invoke sh 'Run cmd 0 :vlax-true))
(vlax-release-object sh)
(setq sh nil)
code)))
(if (vl-catch-all-error-p rc)
(progn
(if sh (vl-catch-all-apply 'vlax-release-object (list sh)))
nil)
rc
)
)
;; --- Erste Zeilen der Python-Logdatei in die Kommandozeile spiegeln ---
;; Damit steht die eigentliche Fehlerursache direkt in BricsCAD und nicht nur
;; in der Logdatei. max-zeilen begrenzt die Ausgabe (Tracebacks koennen lang sein).
(defun export:log-ausgeben (log-pfad max-zeilen / fh zeile n)
(if (setq fh (open log-pfad "r"))
(progn
(setq n 0)
(while (and (setq zeile (read-line fh)) (< n max-zeilen))
(if (/= zeile "")
(progn (princ (strcat "\n | " zeile)) (setq n (1+ n)))
)
)
(close fh)
)
)
(princ)
)
;; --- String fuer JSON escapen (Anfuehrungszeichen/Backslash) --- ;; --- String fuer JSON escapen (Anfuehrungszeichen/Backslash) ---
;; Konvertiert nicht-String-Werte (z.B. T, nil, Zahlen) defensiv in String. ;; Konvertiert nicht-String-Werte (z.B. T, nil, Zahlen) defensiv in String.
(defun csv:json-escape (s) (defun csv:json-escape (s)
@@ -458,7 +567,8 @@
;; include-bbox = T -> Bounding-Box je Block ermitteln und mit exportieren ;; include-bbox = T -> Bounding-Box je Block ermitteln und mit exportieren
;; (siehe csv:block-to-json); fuer EXPORTSIVAS nil. ;; (siehe csv:block-to-json); fuer EXPORTSIVAS nil.
(defun csv:run-export (label py-name csv-name include-bbox / ss i ename fh out-pfad (defun csv:run-export (label py-name csv-name include-bbox / ss i ename fh out-pfad
py-skript ergebnis-pfad cmd first-block count out-dir) py-skript ergebnis-pfad py-exe log-pfad rc first-block
count out-dir)
;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren ;; Vor dem Export: IDs aller Bloecke sicherstellen und Duplikate korrigieren
(princ (ssg-textf "exp-check-ids" (list label))) (princ (ssg-textf "exp-check-ids" (list label)))
(ssg-id-check-all) (ssg-id-check-all)
@@ -521,13 +631,36 @@
(if (findfile py-skript) (if (findfile py-skript)
(progn (progn
(setq ergebnis-pfad (strcat out-dir "/" csv-name)) (setq ergebnis-pfad (strcat out-dir "/" csv-name))
(setq cmd (strcat "python \"" py-skript "\"" (setq py-exe (export:python-exe))
" \"" out-pfad "\"" (setq log-pfad (strcat (cond ((getenv "DXFM_LOG")) (out-dir))
" \"" (getenv "DXFM_DATA") "\"" "/export_python.log"))
" \"" ergebnis-pfad "\"")) (princ (ssg-textf "exp-python-exe" (list label py-exe)))
(princ (ssg-textf "exp-calling-python" (list label))) (princ (ssg-textf "exp-calling-python" (list label)))
(startapp "cmd" (strcat "/c " cmd)) ;; Synchron ausfuehren und Exitcode auswerten - ein stiller
(princ (ssg-textf "exp-export-started" (list label ergebnis-pfad))) ;; Fehlschlag (z.B. WindowsApps-Platzhalter statt Interpreter)
;; darf nicht mehr als "Export gestartet" durchgehen.
(setq rc (export:run-python py-exe py-skript
(list out-pfad (getenv "DXFM_DATA") ergebnis-pfad)
log-pfad))
(cond
((null rc)
(princ (ssg-textf "exp-python-call-failed" (list label))))
((/= rc 0)
(princ (ssg-textf "exp-python-error" (list label rc log-pfad)))
(export:log-ausgeben log-pfad 12)
;; Exitcode 49 (Store-Platzhalter direkt) bzw. 9009 (cmd.exe:
;; "Befehl nicht gefunden") = kein brauchbarer Interpreter.
;; Gezielter Hinweis, wie er korrekt gesetzt wird.
(if (or (= rc 49) (= rc 9009)
(export:python-store-stub-p py-exe))
(princ (ssg-text "exp-python-store-hint"))
))
((not (findfile ergebnis-pfad))
(princ (ssg-textf "exp-csv-missing" (list label ergebnis-pfad log-pfad)))
(export:log-ausgeben log-pfad 12))
(t
(princ (ssg-textf "exp-export-done" (list label ergebnis-pfad))))
)
) )
(princ (ssg-textf "exp-python-not-found" (list label py-skript))) (princ (ssg-textf "exp-python-not-found" (list label py-skript)))
) )
+18
View File
@@ -29,6 +29,23 @@ set "DXFM_RESULTS=%DXFMAKRO%\results"
REM 2D/3D-Modus: Standard ist 3D (alle Module nutzen data\ils\3D\) REM 2D/3D-Modus: Standard ist 3D (alle Module nutzen data\ils\3D\)
set "DXFM_DIM=3D" set "DXFM_DIM=3D"
REM Python-Interpreter fuer die Export-Skripte (EXPORTCSV/EXPORTSIVAS).
REM WICHTIG: BricsCAD erbt diese Umgebung, aktiviert aber kein venv. Ein blankes
REM "python" trifft unter Windows 11 den WindowsApps-Platzhalter (kein echter
REM Interpreter, bricht mit Exitcode 49 ab) - deshalb hier explizit setzen.
set "DXFM_PYTHON="
if exist "%DXFMAKRO%\.venv\Scripts\python.exe" (
set "DXFM_PYTHON=%DXFMAKRO%\.venv\Scripts\python.exe"
) else (
for /f "delims=" %%P in ('py -c "import sys;print(sys.executable)" 2^>nul') do (
if not defined DXFM_PYTHON set "DXFM_PYTHON=%%P"
)
)
if not defined DXFM_PYTHON (
echo HINWEIS: Kein Python gefunden - bin/install_py.bat ausfuehren, sonst
echo liefern EXPORTCSV/EXPORTSIVAS keine CSV-Datei.
)
set BRICSCAD="C:\Program Files\Bricsys\BricsCAD V25 de_DE\bricscad.exe" set BRICSCAD="C:\Program Files\Bricsys\BricsCAD V25 de_DE\bricscad.exe"
if exist "C:\Program Files\Bricsys\BricsCAD V25 de_DE\bricscad.exe" ( if exist "C:\Program Files\Bricsys\BricsCAD V25 de_DE\bricscad.exe" (
@@ -110,6 +127,7 @@ echo DXFM_DIM = %DXFM_DIM%
echo DXFM_BLOCKS = %DXFM_BLOCKS% echo DXFM_BLOCKS = %DXFM_BLOCKS%
echo DXFM_OMNIFLO = %DXFM_OMNIFLO% echo DXFM_OMNIFLO = %DXFM_OMNIFLO%
echo DXFM_DIFFTOOL = %DXFM_DIFFTOOL% echo DXFM_DIFFTOOL = %DXFM_DIFFTOOL%
echo DXFM_PYTHON = %DXFM_PYTHON%
echo ================================================================ echo ================================================================
echo. echo.
+10
View File
@@ -42,6 +42,16 @@ pattern_separator= Separator_SP*, S-LP*
pattern_scanner= Scanner* pattern_scanner= Scanner*
pattern_ks_subblocks= KS_EIN, KS_AUS, K1, K2, K3, K4 pattern_ks_subblocks= KS_EIN, KS_AUS, K1, K2, K3, K4
[python]
# Interpreter fuer die Export-Skripte (lib/export_csv.py, lib/export_sivas.py).
# Leer lassen = automatisch: DXFM_PYTHON (aus bin/setenv.bat) -> Projekt-venv
# <DXFMAKRO>/.venv/Scripts/python.exe -> py.exe -> python (PATH).
# Nur setzen, wenn ein bestimmter Interpreter erzwungen werden muss.
# Wichtig: NICHT auf %LOCALAPPDATA%\Microsoft\WindowsApps\python.exe zeigen -
# das ist nur der Microsoft-Store-Platzhalter (bricht mit Exitcode 49 ab und
# der Export erzeugt dann keine CSV). Solche Pfade werden ignoriert.
interpreter=
[Planquadrate] [Planquadrate]
section=Planquadrate_AN section=Planquadrate_AN
+6
View File
@@ -500,6 +500,12 @@
"exp-calling-python": "\n[%1] Rufe Python auf...", "exp-calling-python": "\n[%1] Rufe Python auf...",
"exp-export-started": "\n[%1] Export gestartet: %2", "exp-export-started": "\n[%1] Export gestartet: %2",
"exp-python-not-found": "\n[%1] FEHLER: Python-Skript nicht gefunden: %2", "exp-python-not-found": "\n[%1] FEHLER: Python-Skript nicht gefunden: %2",
"exp-python-exe": "\n[%1] Interpreter: %2",
"exp-export-done": "\n[%1] Export fertig: %2",
"exp-python-call-failed": "\n[%1] FEHLER: Python konnte nicht gestartet werden (WScript.Shell nicht verfuegbar).",
"exp-python-error": "\n[%1] FEHLER: Python endete mit Exitcode %2. Log: %3",
"exp-python-store-hint": "\n HINWEIS: Es wurde kein echter Python-Interpreter gefunden ('python' zeigt\n unter Windows 11 auf den WindowsApps-Platzhalter). Loesung: bin/install_py.bat\n ausfuehren (legt .venv an) und BricsCAD ueber bin/start_briscad.bat neu\n starten, oder in cfg/export.cfg unter [python] interpreter= den vollen Pfad\n zur python.exe setzen.",
"exp-csv-missing": "\n[%1] FEHLER: Python meldete Erfolg, aber die CSV fehlt: %2. Log: %3",
"exp-omni-no-inserts": "\n[OMNI_UPDATE] Keine INSERT-Bloecke in der Zeichnung.", "exp-omni-no-inserts": "\n[OMNI_UPDATE] Keine INSERT-Bloecke in der Zeichnung.",
"exp-omni-updated": "\n[OMNI_UPDATE] %1 Elemente aktualisiert (HOEHE, DREHUNG).", "exp-omni-updated": "\n[OMNI_UPDATE] %1 Elemente aktualisiert (HOEHE, DREHUNG).",
"id-new-assigned": "\n[ID] Neue ID zugewiesen: %1", "id-new-assigned": "\n[ID] Neue ID zugewiesen: %1",
+6
View File
@@ -500,6 +500,12 @@
"exp-calling-python": "\n[%1] Calling Python...", "exp-calling-python": "\n[%1] Calling Python...",
"exp-export-started": "\n[%1] Export started: %2", "exp-export-started": "\n[%1] Export started: %2",
"exp-python-not-found": "\n[%1] ERROR: Python script not found: %2", "exp-python-not-found": "\n[%1] ERROR: Python script not found: %2",
"exp-python-exe": "\n[%1] Interpreter: %2",
"exp-export-done": "\n[%1] Export finished: %2",
"exp-python-call-failed": "\n[%1] ERROR: Could not start Python (WScript.Shell unavailable).",
"exp-python-error": "\n[%1] ERROR: Python exited with code %2. Log: %3",
"exp-python-store-hint": "\n NOTE: No real Python interpreter was found (on Windows 11 'python' points\n to the WindowsApps placeholder). Fix: run bin/install_py.bat (creates .venv)\n and restart BricsCAD via bin/start_briscad.bat, or set the full path to\n python.exe in cfg/export.cfg under [python] interpreter=.",
"exp-csv-missing": "\n[%1] ERROR: Python reported success but the CSV is missing: %2. Log: %3",
"exp-omni-no-inserts": "\n[OMNI_UPDATE] No INSERT blocks in the drawing.", "exp-omni-no-inserts": "\n[OMNI_UPDATE] No INSERT blocks in the drawing.",
"exp-omni-updated": "\n[OMNI_UPDATE] %1 elements updated (HEIGHT, ROTATION).", "exp-omni-updated": "\n[OMNI_UPDATE] %1 elements updated (HEIGHT, ROTATION).",
"id-new-assigned": "\n[ID] New ID assigned: %1", "id-new-assigned": "\n[ID] New ID assigned: %1",