[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:
+140
-7
@@ -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)))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user