Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu
Online

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.115 Beiträge
 
Delphi 12 Athens
 
#1

Python im Delphi ausführen (geht nicht)

  Alt 21. Jan 2021, 13:28
Moin,

nutzt hier irgenwer Python direkt in seiner Anwendung?

https://github.com/pyscripter/python4delphi
https://github.com/python/cpython
https://www.python.org/downloads/


Aktuell haben wir noch die Python.exe, wo Input und Output ins Programm umgeleitet sind (CreateProcess)
und jetzt wollten wir uns direkt mit der Python*.dll verbinden.

Erstmal, da dort die Fehlerbehandlung einfacher würde und vor allem auch für die Übergabe von Variablen. (später auch Zugriff auf Funktion und Klassen im Delphi)


Derzeit spielen wir etwas mit Python4Delphi rum
und wöllten eigenlich am Liebsten das Python direkt aus "unserem" Programmverzeichnis nehmen, anstatt "irgendeiner" (nicht)installierten Version. (auf den Rechnern unserer Kunden)

DLLName und DLLPath kann man "eigentlich" in der TPythonEngine angeben,
aber es raucht wortlos ALLES ab. (nur mit einer "richtigen" Installation läuft es bisher)

Bisher sind wir so weit, dass wir wissen es bricht im Py_Initialize ab.
Wir hatten auch gehört, dass Py_Initialize blöd ist und man besser Py_InitializeEx verwenden soll. (geändert, aber natürlich kein Unterschied)
Im Assmbler dann etweas reingedebuggt und es bricht im letzten CALL innerhalb von Py_InitializeEx ab,
dann den Quellcode besorgt und ....

und jetzt würde ich die Python-Entwickler liebendgern erwürgen
und den Entwickler von Python4Delphi auch ein bisschen.



Wie sind denn eure Erfahrungen?



Wir wissen noch nicht warum, aber wenigstens nun erstmal wo es abraucht.

Also genauer beendet sich das Programm, sobald die Form mit dieser Komponente geladen wird.
Keine Exception ... einfach zu und weg.

TPythonEngine.AfterLoad -> TPythonEngine.Initialize -> Py_Initialize -> Py_InitializeFromConfig -> ...
... -> _PyStatus_EXCEPTION -> Py_ExitStatusException -> Exit oder Write(stderr)+Exit

Code:
// pylifecycle.c

void
Py_InitializeEx(int install_sigs)
{
    PyStatus status;

    status = _PyRuntime_Initialize();
    if (_PyStatus_EXCEPTION(status)) {
        Py_ExitStatusException(status);
    }
    _PyRuntimeState *runtime = &_PyRuntime;

    if (runtime->initialized) {
        /* bpo-33932: Calling Py_Initialize() twice does nothing. */
        return;
    }

    PyConfig config;
    _PyConfig_InitCompatConfig(&config);

    config.install_signal_handlers = install_sigs;

    status = Py_InitializeFromConfig(&config);
    if (_PyStatus_EXCEPTION(status)) {
        Py_ExitStatusException(status);
    }
}

void _Py_NO_RETURN
Py_ExitStatusException(PyStatus status)
{
    if (_PyStatus_IS_EXIT(status)) {
        exit(status.exitcode);
    }
    else if (_PyStatus_IS_ERROR(status)) {
        fatal_error(stderr, 1, status.func, status.err_msg, 1);
    }
    else {
        Py_FatalError("Py_ExitStatusException() must not be called on success");
    }
}

void
Py_Initialize(void)
{
    Py_InitializeEx(1);
}


Fazit: Als "Fehlerbehandlung" beendet diese besch**** Komponente einfach so den kompletten Prozess (Halt/Exit/ExitProcess), anstatt z.B. eine Exception zu werfen (oder etwas mit Fehlercodes, innerhalb des Programms).

Wie im fatal_error zu sehen, würde ein Fehlertext eventuell auf die Console (strerr) ausgegeben.
Natürlich geil, dass eine VCL-Anwendung keine Console hat und selbst mit Console geht das Fenster bei Programmende auch sofort weg. (müsste man das dann also auch noch in der CMD.exe starten)


Auch im PascalWrapper (PythonEngine.pas) ist böswillig in ExitProcess versteckt,
denn das Property PythonEngine.FatalAbort ist standardmäßig True.

Delphi-Quellcode:
// wenn Fehler aufgetreten und Property FatalAbort=True
if FatalAbort then
  Quit;


procedure TDynamicDll.Quit;
begin
  if not( csDesigning in ComponentState ) then begin
{$IFDEF MSWINDOWS}
    MessageBox( GetActiveWindow, PChar(GetQuitMessage), 'Error', MB_TASKMODAL or MB_ICONSTOP );
    ExitProcess( 1 );
{$ELSE}
    WriteLn(ErrOutput, GetQuitMessage);
    Halt( 1 );
{$ENDIF}
  end;
end;

Wie kann man nur so dermaßen böswillig sein
und, nur weil es ein kleines Problem gibt, gleich den kompletten Prozess abwürgen,
anstatt nur, in dem einen Thread, einen Fehler zu werfen, welcher abgefangen und verarbeitert werden könnte?



Besonders cool ist auch die DLL-Suchfunktion der TPythonEngine (UseLastKnownVersion), welche auf einem Testrechner mit Installiertem Python (64 Bit) dessen DLL findet,
welche ... naja, in einem 32 Bit Programm geladen, geht es irgendwie nicht gut.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests

Geändert von himitsu (21. Jan 2021 um 13:44 Uhr)
  Mit Zitat antworten Zitat