Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Werkzeuge (https://www.delphipraxis.net/63-sonstige-werkzeuge/)
-   -   Wie Embedded-Python und PIP? (https://www.delphipraxis.net/213346-wie-embedded-python-und-pip.html)

himitsu 13. Jul 2023 22:33


Wie Embedded-Python und PIP?
 
Ich würde den PackageManager gern benutzen, aber er hasst mich. :cry:




Gegeben: Python 3.8.10 Embedded 32-bit + Python4Delphi
Das P4D damals aus GitHub, bzw. hier aus'm Forum (nicht das Neue vom GetIt, k.A. ob es da Unterschiede gibt).

Also im Prinzip das https://www.python.org/ftp/python/3....mbed-win32.zip als pures Verzeichnis.

(Unser Python4Delphi ist zwar bissl angepasst, damit es mit dem Embedded besser geht, weil der Entwickler vorrangig auf ein installiertes Python bedacht war, aber egal ... wir wollten einfach keine externen Abhängigkeiten, wenn z.B. der Kunde bei sich eine andere Python-Version installiert)




Und nun versuchte ich dort via PIP etwas zu "installieren".
z.B.
Delphi-Quellcode:
python.exe -m pip install requests
des https://pypi.org/project/requests/
So geht das erstmal natürlich nicht, denn es fehlt ja das Modul "pip".

Für das Nachfolgende ist das Embedded (EXE oder DLL im Delphi) egal irrelevant. (unnötig aufwändig, extra noch eine Test-Anwendung in Delphi)
Die python.exe wird also erstmal als "Portable" benutzt. (macht für die Funktion keinen Unterschied)


Einige Standardsachen (Python-Scripts und Modules) liegen als *.pyc in der python38.zip, sowie als *.pyd im Verzeichnis)


PS: Ja, im Python wird ein Modul "request" mitgeliefert (python38.zip), aber benötigt wird "requests". :stupid:




Versuch 1: https://pip.pypa.io/en/stable/installation/#get-pip-py

Die https://bootstrap.pypa.io/get-pip.py runtergeladen, ins Verzeichnis gelegt
und
Delphi-Quellcode:
python.exe get-pip.py
macht etwas, lädt es runter und es sieht so aus, als würde es installiert.
Code:
Collecting pip
  Downloading pip-23.1.2-py3-none-any.whl (2.1 MB)
     ------------------------------------------------- 2.1/2.1 MB 11.9 MB/s eta 0:00:00
Collecting setuptools
  Downloading setuptools-68.0.0-py3-none-any.whl (804 kB)
     ------------------------------------------------- 804.0/804.0 kB 24.8 MB/s eta 0:00:00
Collecting wheel
  Downloading wheel-0.40.0-py3-none-any.whl (64 kB)
     ------------------------------------------------- 64.5/64.5 kB ? eta 0:00:00
Installing collected packages: wheel, setuptools, pip
  WARNING: The script wheel.exe is installed in 'C:\*****\Python3\Scripts' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
  WARNING: The scripts pip.exe, pip3.8.exe and pip3.exe are installed in 'C:\*****\Python3\Scripts' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed pip-23.1.2 setuptools-68.0.0 wheel-0.40.0
es sind in .\Scripts\ ein paar *.exe aufgetaucht
und unter .\Lib\site-packages\ Unterverzeichnisse mit den 3 runtergeladenen Modulen und weitere Dateien/Verzeichnisse von der Packageverwaltung.

aber
Delphi-Quellcode:
python.exe -m pip install requests
sagt immernoch, dass es kein PIP gäbe.
Zitat:

python.exe: No module named pip
sowie auch
Delphi-Quellcode:
.\Scripts\pip.exe
sagt das Gleiche.
Zitat:

ModuleNotFoundError: No module named 'pip'



So, also nun Versuch 2: ebenfalls https://pip.pypa.io/en/stable/installation/#get-pip-py

Die https://bootstrap.pypa.io/pip/pip.pyz runterladen
und
Delphi-Quellcode:
python.exe pip.pyz install requests

Code:
Collecting requests
  Downloading requests-2.31.0-py3-none-any.whl (62 kB)
     ------------------------------------------------- 62.6/62.6 kB 1.6 MB/s eta 0:00:00
Collecting charset-normalizer<4,>=2 (from requests)
  Downloading charset_normalizer-3.2.0-cp38-cp38-win32.whl (89 kB)
     ------------------------------------------------- 89.4/89.4 kB 2.6 MB/s eta 0:00:00
Collecting idna<4,>=2.5 (from requests)
  Downloading idna-3.4-py3-none-any.whl (61 kB)
     ------------------------------------------------- 61.5/61.5 kB 814.3 kB/s eta 0:00:00
Collecting urllib3<3,>=1.21.1 (from requests)
  Downloading urllib3-2.0.3-py3-none-any.whl (123 kB)
     ------------------------------------------------- 123.6/123.6 kB 3.7 MB/s eta 0:00:00
Collecting certifi>=2017.4.17 (from requests)
  Downloading certifi-2023.5.7-py3-none-any.whl (156 kB)
     ------------------------------------------------- 157.0/157.0 kB 2.3 MB/s eta 0:00:00
Installing collected packages: urllib3, idna, charset-normalizer, certifi, requests
  WARNING: The script normalizer.exe is installed in 'C:\*****\Scripts' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed certifi-2023.5.7 charset-normalizer-3.2.0 idna-3.4 requests-2.31.0 urllib3-2.0.3
Hier wird "scheinbar" auch das Gewünschte runtergeladen,
also in .\Scripts\ eine *.exe
und unter .\Lib\site-packages\ das "requests" und dessen Requires (certifi, charset_normalizer, idna und urllib3).

Aber es geht dann dennoch nicht, also wird nicht gefunden.




Dann jetzt mal Testen. (Anhand dem 2. Versuch)
Per Parameter, aber ginge auch als PY-Script.

Delphi-Quellcode:
python -c "print('example')"

oder
Delphi-Quellcode:
print('example')
als test.py und
Delphi-Quellcode:
python test.py

perfekt
und es gibt auch schön das
Delphi-Quellcode:
example
aus.

Mit einem Modul (aus der python38.zip) geht's auch.
Delphi-Quellcode:
python -c "import uuid; print(uuid.uuid4())"
=>
Delphi-Quellcode:
5d6615d3-b65e-467c-b6ca-dfbf6fe7f874


Ebenfalls mit einem Modul von außerhalb. ( _hashlib.pyd im Verzeichnis, nicht hashlib.pyc aus der python38.zip )
Delphi-Quellcode:
python -c "import _hashlib; print(_hashlib.openssl_md5('aaa'.encode('utf-8')).hexdigest())"
=>
Delphi-Quellcode:
47bce5c74f589f4867dbd57e9ca9f808





Nun aber unser neues Modul.
Delphi-Quellcode:
python -c "import requests"
ergibt das bekannte Bild
Zitat:

ModuleNotFoundError: No module named 'requests'

Die Hilfe war auch keine Hilfe. https://docs.python.org/3/reference/import.html
Delphi-Quellcode:
python -c "import Lib.site-packages.requests"
will dennoch nicht, denn es mag keine - und ' oder " in sonstwelchen Varianten ändert sowieso nichts.
Außerdem wäre es eh blöd, was IMPORTs in Fremd-Komponenten anginge.


Die Modul-Verzeichnisse von
Delphi-Quellcode:
.\Lib\site-packages\
direkt ins Python-Verzeichnis
Delphi-Quellcode:
.
verschoben/kopiert geht zwar,
Delphi-Quellcode:
python -c "import requests; r=requests.get('https://github.com/timeline.json'); print(r.status_code,' ',r.text)"

aber da könnte ich mir das PIP doch auch sparen und diese Module/Verzeichnisse manuell runterladen. :freak:

Phoenix 14. Jul 2023 09:21

AW: Wie Embedded-Python und PIP?
 
Schau Dir mal das Konzept von virtuellen Environments bei Python an.

Solange Du nicht explizit in einem virtuellen Environment bist, weiss PIP das nicht und installiert grundsätzlich alles global (Womit Du eine bereits bestehende Installation ggf. zerschiessen könntest, weil Du ggf. packages in anderen Versionen installierst als die, die ein existierendes Stück Software braucht).
Und wenn Du selber ein so umgebogenes Python hast, das eben nicht die "normalen" globalen Packages verwendet, findet es die dann eben auch nicht.

himitsu 14. Jul 2023 10:49

AW: Wie Embedded-Python und PIP?
 
Gut, ich hab hier grade kein "installiertes" Python drauf, aber zumindestens sieht es erstmal so aus, als wenn PIP "hier nur" in Unterverzeichnisse was reinpappt. (könnte vielleicht mal noch ein Python installieren und dann processmonitoren)

Im Moment sieht es so aus, als wenn ich die pip.pyz zumindestens nutzen könnte, zum Runterladen inkl. der Abhängigkeiten.
Und das dann manuell ins Hauptverzeichnis verschieben müsste, damit es genutzt werden kann.

Kollege hatte es (vor)gestern alles manuell gemacht.
* das gewünschte Python-Script besorgt
* ausprobiert und geschaut was fehlt
* das dann gesucht und z.B. von GitHub runtergeladen
* wieder ausprobiert, was jetzt noch fehlt usw.

Aktuell war mein Plan, sein Runtergeladenes bei uns ins GitRepo einzufügen, damit das Setup es auch in den Installationen verteilt
und die pip.pyz zu belassen, damit man zumindestens den automatischen Download nutzen könnte (und dann halt verschieben).




[ADD]

Boar eh, gleich das erste gesponsorte Suchergebnis im G "Wissenschaftliche Artikel zu python virtuellen Environments" ... da bekommt man doch Angst?

Bezüglich PIP nichts im Python4Delphi gefunden,
aber zum VENV bzw. VirtualEnv, welches am Ende auf eine pyvenv.cfg hinaus läuft.

https://geekflare.com/de/virtual-environments-python/
https://docs.python.org/3/library/venv.html

Ich glaub innerhalb dieser/unseren "portablen Installation" brauchen wir aktuell wohl noch kein VirtualEnvironment.
-> mehrere getrennte Umgebungen für unsere python.exe/dll

Außer vielleicht, wenn es eine virtuelle Umgebung wird, welche auch dieses Verzeichnis benutzt. :angle2:



Gefühlt brauchen wir doch "bloß" irgendwo einen Suchpfad, damit Python auch in diesem PIP-Verzeichnis sucht?
Also das "import requests" nicht nur in .\requests.* oder .\python38.zip\requests.*, sondern auch im .\Lib\site-packages\requests.*

Phoenix 14. Jul 2023 11:15

AW: Wie Embedded-Python und PIP?
 
Ja, Python verwendet viel an Umgebungsvariablen, die u.a. eben auch durch die virtual environments gesteuert / belegt werden.
Python muss wissen wo es nach modulen suchen muss, sonst sucht es nur global an seinen default-Orten.
Durch andere variablen wird allerdings für PIP definiert, wo es drauf arbeitet, sonst nimmt es seine default-Orte.

Die Python- und die PIP-Defaults sind aber nicht zwangsläufig identisch (bzw. vermutlich nur mehr oder weniger zufällig, wenn Du in einer globalen Installation arbeitest).

Das virtual env macht im Prinzip nix anderes, als die für diese Dinge verwendeten Umgebungsvariablen zu setzen, inkl. auch den PATH so anzupassen dass die jeweiligen python- und pip executables des aktuellen Envs genommen werden (weil Du pro virtuellem environment ja auch unterschiedliche Python-Versionen haben kannst).

Du musst keine virtuellen envs verwenden, aber in dem Kontext in dem Python dann ausgeführt wird müssen halt zumindest alle notwendigen Umgebungsvariablen auf die richtigen Verzeichnisse zeigen und Du musst sicherstellen, dass die PATH-Angabe deine lokalen Verzeichnisse vor den globalen listet, damit nicht versehentlich ein globales pip mit lokalem python oder umgekehrt verwendet wird.

himitsu 14. Jul 2023 12:50

AW: Wie Embedded-Python und PIP?
 
Danke nochmal, für's Stupsen in die passende Richtung. :thumb:


Ahhh, es gibt die python38._pth
da stehen die Suchpfade drin, also "python38.zip" und "."
und NEU nun von mir auch "Lib\site-packages"
sowie ein "Addons", wo ich grade die zusätzlichen Scripte reinschiebe (damit liegt in "." nur noch der Inhalt der python-3.8.10-embed-win32.zip )

Außerdem kann man dort auch automatische Imports und ein InitialScript hinterlegen.

(muß man nur drauf achten beim nächsten Upgrade das nicht zu überschreiben, bzw. nicht zu vergessen es in die neue ._pth zu übernehmen :freak:)

Zitat:

Zitat von python38._pth
Code:
python38.zip
.
Addons
Lib\site-packages

# Uncomment to run site.main() automatically
#import site

Ja, hab PIP und Unseres nach dem Standardzeugs. :oops:






Wo ich langsam immer mehr Suchbegriffe zusammen hatte, fand sich nun auch endlich etwas .....

(ur)alter Weg:
Code:
import sys
py_ext_path = r"C:\Users\bob\AppData\Local\Programs\Python\Python38\Lib\site-packages"
if py_ext_path not in sys.path:
    sys.path.append(py_ext_path)
Quelle: https://www.originlab.com/doc/python/Python-Packages

oder die Umgebungsvariable PYTHONPATH
oder
Code:
import sys
sys.path.append('.\Lib\site-packages')
import mod
oder neu eben die python38._pth :firejump:

https://docs.python.org/3/library/venv.html
https://docs.python.org/3/using/cmdl...var-PYTHONPATH

https://en.delphipraxis.net/topic/51...-installation/
https://stackoverflow.com/questions/...on-for-windows

https://stackoverflow.com/questions/...-in-virtualenv

https://www.youtube.com/watch?v=b093aqAZiPU

himitsu 21. Jul 2023 10:30

AW: Wie Embedded-Python und PIP?
 
Liste der Anhänge anzeigen (Anzahl: 1)
:hi:

Zitat:

Hallo Frank, im Master sind bei den Python-Files einige komische Dateien reingekommen. Diese führen nun teilweise zu Zirkelbeziehungen und führen damit zu Fehlern.
Siehe Bild im Anhang.

Im Grunde ist das der Unterschied zwischen manuellem Download und dem Installieren via PIP, wo zusätzliche Dateien aufgetaucht sind.

Weiß jemand, was Diese darstellen sollen?


Anhang 56146
Ja, die dist-infos sind klar,
aber diese markierten PYD :?:

Sie sind im Download nicht da und hier auch ausschließlich innerhalb dieses einen Packages.
Gelöscht und neu installiert, da sind sie auch schon drin. (hätte sein können, dass es noch Reste meiner Tests waren)



Klar, einfach löschen und weg ist das Problem, aber ich wüsste gern warum, vorallem wenn sie beim nächsten Update wieder da sein sollten.




Die genaue Fehlermeldung kann ich noch nicht liefern.
Kollege will dann nochmal bei sich das Verzeichnis zurücksetzen und wenn er den Fehler wieder sieht ........

BastiFantasti 10. Okt 2023 09:06

AW: Wie Embedded-Python und PIP?
 
Wie ist denn nun der bevorzugte Weg, die PIP Abhängigkeiten automatisch mit zu installieren?
Hast du da was in der ._pth Datei ergänzt und wenn ja, was :wink:

Und wie wird das getriggered? Automatisch beim Erzeugen der PythonEngine?

Viele Grüße
Bastian

himitsu 10. Okt 2023 12:31

AW: Wie Embedded-Python und PIP?
 
Ich fürchte viele sind Linux-Entwickler oder nutzen ein großes installiertes Python,
wo bereits einige Module vorinstalliert sind und welche dann fast alle vergessen in ihre Requires eintzutragen.

Steht dann aber meistens irgendwo in der Mitte der Logausgabe, wo Fehlermeldung+Stacktrace gern 1-2 Bildschime lang ist.



das Python von dort https://www.python.org/downloads/windows/
z.B. https://www.python.org/ftp/python/3....mbed-win32.zip

dann das https://pip.pypa.io/en/stable/installation/#get-pip-py
also die ttps://bootstrap.pypa.io/pip/pip.pyz ins Verzeichnis (das ist eine ZIP, wo alles drin ist ... ABER nicht auspacken)
oder die https://bootstrap.pypa.io/get-pip.py ins Verzeichnis und ausführen
Delphi-Quellcode:
python get-pip.py

leider lädt Letzteres das PIP-Modul auch nur runter, aber vergißt ebenfalls den Suchpfad (python*._pth)

z.B. python38._pth
Code:
python38.zip
.
Addons
Lib\site-packages

# Uncomment to run site.main() automatically
#import site
Addons ist von mir (manuell runtergeladene *.py, damit die nicht im RootVerzeichnis rumkullern und ein Upgrade einfacher ist)
Lib\site-packages ist für das Runtergeladene vom PIP
und Nachfolgendes könnte man für automatische Initialisierungen benutzen

Code:
@prompt @@$G$S
@cd /d "%~dp0"

@echo.
@echo Aktuelle pip.pyz downloaden.
@echo.
@echo Info:  PIP ist nicht als Python-Modul via PIP installiert,
@echo        sonden liegt als gepackte Python-Application vor.
@echo.
@pause
@echo.

del pip.pyz
curl https://bootstrap.pypa.io/pip/pip.pyz --output "pip.pyz"

@echo.
@pause
und die https://www.python.org/ftp/python/3....mbed-win32.zip könnte man auch via CURL runterlade und entpacken
(aber aufpassen, denn da ist python*._pth drin und würde somit überschrieben)

Delphi-Quellcode:
python pip.pyz <command> <params>

bzw.
Delphi-Quellcode:
python -m pip <command> <params>

Code:
python pip.pyz help                     Hilfe
python pip.pyz list                     Liste des Installierten
python pip.pyz search                   Suchen : geht nicht mehr, aber siehe https://pypi.org/search bzw. https://pypi.org/classifiers/

python pip.pyz install <modulname>      Installieren
python pip.pyz uninstall <modulname>    Deinstallieren
python pip.pyz show <modulname>         Infos: Abhängigkeiten, Version, Beschreibung, Quelle usw.

python pip.pyz check                    Abhängigkeiten prüfen
Elso einfach im Explorer in die Adresszeile "CMD" schreiben, [Enter] und dann schön Rumbefehlen.




PIP läd auch ein paar Hilfs-EXEn runter, welche über die Suchpfade gesucht werden, anstatt mit einem relativem Pfad zu arbeiten,
daher dann noch

in Console/Batch:
Code:
@set PATH=%~dp0Scripts;%PATH%
@set PYCACHE=
::@set PYTHONPATH=irgendwas.......
python.exe pip.pyz install
durch das PYCACHE= wird der Download-Cache des PIP deaktiviert :oops:

sowie beim Laden des Python im Delphi:
Delphi-Quellcode:
P := EnviromentVar('PATH', False);
S := ExtractFilePath(FPythonDLL) + 'Scripts';
if DirectoryExists(S) and not ContainsText(P, S) then
  SetEnvironmentVariable('PATH', PChar(S + ';' + P));

BastiFantasti 10. Okt 2023 14:38

AW: Wie Embedded-Python und PIP?
 
Danke für die ausführliche Antwort. Das heißt du installierst die Requirements / Pakete immer über die Kommandozeile direkt in diesem embedded Python Verzeichnis.

Ich hab gedacht / befürchtet, dass das über ein TPythonEngine.ExecuteIrgendwas direkt ausgeführt werden kann / muss.
Aber über die Shell sollte das klappen.

Ich teste und berichte :)

himitsu 10. Okt 2023 15:07

AW: Wie Embedded-Python und PIP?
 
Man könnte es auch über ein Python-Script im Python machen. (ob über die Console oder die DLL wäre egal .... ich weiß nur nicht wie man dort die Parameter übergibt)

TPythonEngine selbst kennt ja das PIP nicht direkt, drum ist dort auch nichts eingebaut.

Bezüglich der EnvironmentVariablen ... die kann man auch ans ShellExecute/CreateProcess übergeben (komisch, ich dachte es gab auch einen Parameter an ShellExecute/ShellExecuteEx)






mit SET & den CALL als "Script" an die CMD.exe

oder SetEnvironmentVariable (im eigenen Process) und dann ShellExecute/ShellExecuteEx

oder SetEnvironmentVariable und dann mit GetEnvironmentStrings an CreateProcess:lpEnvironment

oder GetEnvironmentStrings + die Variablen und dann an CreateProcess:lpEnvironment

oder ...

0KommaNix 11. Jan 2024 13:30

AW: Wie Embedded-Python und PIP?
 
Hallo himitsu,
ich bin durch einen Verweis - bzw. Antwort - von dir auf dieses Thema aufmerksam geworden. https://www.delphipraxis.net/1531416-post11.html
Ich kenne bisher nur venv. Nun möchte ich die Vor- und Nachteile gegenüber "Embedded" untersuchen. Das Ziel ist klar: Man will ohne viel Aufwand und Stolperfallen eine Installation auf den Benutzer-PC hinbekommen. Bei beiden Varianten hat man ein Verzeichnis, wo alles - was für Python benötigt wird - enthalten ist. Wenn ich es richtig verstehe, dann braucht man bei Embedded kein Skript zum Starten, wie es bei venv notwendig ist. Dafür braucht man sich bei venv nicht um die Installation von pip kümmern. Was aber auch nur einmal erfolgen muss. Danach kann pip sich ja selbst updaten. Bei Embedded scheint es zu genügen, dass man den Path zur gewünschten python.exe in P4D über ein relative Path-Angabe setzt. Mich verunsichert aber, dass du schriebst:

Zitat:

Zitat von himitsu (Beitrag 1524566)
Ich würde den PackageManager gern benutzen, aber er hasst mich. :cry:
(Unser Python4Delphi ist zwar bissl angepasst, damit es mit dem Embedded besser geht, weil der Entwickler vorrangig auf ein installiertes Python bedacht war, aber egal ... wir wollten einfach keine externen Abhängigkeiten, wenn z.B. der Kunde bei sich eine andere Python-Version installiert)

Das lässt mich befürchten, dass es bei Verwendung von Python4Delphi zu Problemen kommen kann. Kannst du bitte deine Erfahrung mit P4D+Embedded Python näher erläutern?

himitsu 11. Jan 2024 14:52

AW: Wie Embedded-Python und PIP?
 
Nja, erstmal haben wir ein paar Bugfixe im python4delphi. (könnte bei Gelegenheit mal nachsehn, wie ich unseren firmeninternen Github-Fork in einen Öffentlichen umkopiere)

Dann ist das Ganze noch in eine Python-Klasse eingebettet, welche das Suchen/Laden der DLL übernimmt, die eigentliche Python-Klasse und ein paar Module/Funktionen intern erstellt/verwaltet.
Im Endeffekt kommt bei der Suche sowas raus, was man auch manuell zuweisen könnte. (aus einer der DFM der Demoanwendungen kopiert)
Code:
  object PythonEngine1: TPythonEngine
    DllName = 'python39.dll'
    DllPath = 'E:\EXE\Python'
    APIVersion = 1013
    RegVersion = '3.9'
    UseLastKnownVersion = False
    ...
Für das PIP im Embedded hatte ich zwei Lösungen gefunden
* PIP als Modul im PIP selbst installiert (wird über ein initialies Python-Script "teilweise" eingerichtet)
* oder PIP als Python-Application (PYZ)
* Letzeres haben wir bei uns im Einsatz

[das Python]
Infos: https://wiki.python.org/moin/EmbeddedPython
Quelle: https://www.python.org/downloads/windows/
Download: https://www.python.org/ftp/python/3....mbed-win32.zip

[als App]
Quelle: https://pip.pypa.io/en/stable/installation/#get-pip-py
Download: https://bootstrap.pypa.io/pip/pip.pyz
Nutzung:
Delphi-Quellcode:
python.exe pip.pyz <command> <params>


[als Modul]
Installaionsscript: https://bootstrap.pypa.io/get-pip.py ->
Delphi-Quellcode:
python.exe get-pip.py

Nutzung:
Delphi-Quellcode:
python.exe -m pip <command> <params>

z.B.
Delphi-Quellcode:
python pip.pyz install <modulname>


In der python38._pth die Suchpfade für's PIP hinzufügen (für Suche der Python-Scripte&Module)
Code:
python38.zip
.
Addons
Lib\site-packages
und dann auch nochmal zur Laufzeit der Komponente/DLL (für Helper-EXEn welche die Module mitbringen)
Delphi-Quellcode:
// PIP-ScriptHelper in Suchpfad
P := EnviromentVar('PATH', False);
S := ExtractFilePath(FPythonDLLFilename) + 'Scripts';
if DirectoryExists(S) and not ContainsText(P, S) then
  SetEnvironmentVariable('PATH', PChar(S + ';' + P));

Probleme haben wir beim Embedded aber mit Pyhon-Komkomenten aus'm PIP, welche wiederum selbst DLLs laden,
da diese DLLs, nach Ausführung des Scripts, "meistens" garnicht oder nicht richtig wieder entladen werden.
Und beim nächsten Ausführen eines anderen Scriptes es dann blöde Fehler gibt, auch bis dahin, dass garnichts mehr geht. Oder es mindestens Speicherlecks gibt, welche sich bis zum Kollaps aufsummieren.



Hier ein Auszug der Info-Datei, welche ich mal bei uns im Projekt hinterlegt hab (bissl Firmeninternes entfernt/anonymisiert)
Code:
Download         : https://www.python.org/downloads/windows/
                     -> "Windows embeddable package (32-bit)", da Delphi&UnserProgramm als Win32
                     https://www.python.org/ftp/python/3.8.10/python-3.8.10-embed-win32.zip

aktuelle Version : Python 3.8.10 Embedded 32-bit

Python-UPGRADE   : aktuelles siehe "Download"
                    ins DIESES Verzeichnis "Python3" entpacken
                    Kann alles gelöscht/überschrieben werden, siehe "restliche *.pyd"
                    ABER in der python**._pth die Suchpfade beachten, siehe "python38._pth"
                    ...
                    und zuletzt siehe "python pip.pyz"

                    LÖSCHEN: charset_normalizer\md.cp38-win32.pyd und charset_normalizer\ md__mypyc.cp38-win32.pyd
                       werden durch PIP in den Download eingefügt und führen zu Problemen.
                       https://www.delphipraxis.net/1524779-post6.html

SourceCode der Python**.dll -> https://github.com/python/cpython -> \Include\pylifecycle.h



[[ Anleitung PIP : Package Installer for Python ]]

Installation siehe "pip.pyz"
Explorer > ProgrammVerzeichnis\Python3
in Adressleiste : CMD [Enter]

python pip.pyz help                     Hilfe
python pip.pyz list                     Liste des Installierten
python pip.pyz search                   Suchen : geht nicht mehr, aber siehe https://pypi.org/search bzw. https://pypi.org/classifiers/

python pip.pyz install <modulname>      Installieren
python pip.pyz uninstall <modulname>    Deinstallieren
python pip.pyz show <modulname>         Infos: Abhängigkeiten, Version, Beschreibung, Quelle usw.

python pip.pyz check                    Abhängigkeiten prüfen



[[ Einiges vom Verzeichnisinhalt des "Python3" ]]

python.exe        = nutzen wir im Programm nicht (aber für PIP oder z.B. manuelles Ausführen der DebugBackup-Datei *.py aus Programm)
python3.dll       = Forward-DLL mit Weiterleitung zur jeweiligen python3*.dll
python38._pth     = Python-Config : Suchpfade und optional ein InitialScript
                         "python38.zip" , "." und unser Zusatz für's PIP "Lib\site-packages" , sowie ein "Addons"
python38.dll      = [Version 3.8.10]  das Python
python38.zip      = [Version 3.8.10]  viele StandardModule (os, io, datetime, zip, base64, xml, json, csv, email, ...)
                   = ist ein site-package (wird über python39._pth geladen) und enthält viele *.pyc (compiled bytecode)
                         diese können ebenfalls in den eigenen Scripten genutzt werden
*.pyd             = vorkompilierte Python-Module (DLLs), zur Nutzung innerhalb der Scripte
*.pyc             = kompiliertes Python-Script
*.py              = Python-Script
*.pyz             = Python Zipped Executable ist wohl sowas Ähnliches wie *.pyd

restliche *.pyd   = stammen aus der python-3.8.10-embed-win32.zip, also ebenfalls Standard-Zeugs, wie die python38.zip
                     -> bis auf DIESE Info-Datei, bzw. die Versions-Info, die "pip.pyz" und unsere angepasste "python38._pth"

__pycache__\       = [gitignore] Cache der Python-EXE/DLL (kompilierte *.pyc der *.py)

Addons\            = Zusätzliche Scripte von uns (siehe Nachfolgend "Addons")
Lib\site-packages\ = Python-Module via PIP installiert
Scripts\           = Tools vom PIP

pip.pyz           = [Version 3.6]     Package Installer for Python - der Paketmanager (siehe "Anleitung PIP")
                                                               Quelle:    https://pip.pypa.io/en/stable/installation/#get-pip-py
                                        [als Python-Anwendung] Download:  https://bootstrap.pypa.io/pip/pip.pyz
                                                               muß ins Hauptverzeichnis, geht nicht aus .\Addons\
                                                               "CMD: python.exe pip.pyz <command> <params>"
                                        [als Python-Modul]    Alternativ: https://bootstrap.pypa.io/get-pip.py
                                                               dieses runterladen und installieren "python.exe get-pip.py"
                                                               landet im .\Lib\site-packages\
                                                               "CMD: python.exe -m pip <command> <params>"

himitsu 11. Jan 2024 14:55

AW: Wie Embedded-Python und PIP?
 
Beim Python4Delphi ist eigentlich eine VCL-Erweiterung dabei, aber wir hatten noch nicht rausbekommen, wie man die benutzt.
Klingt ja so, als wenn sie die VCL des eigenen Programms nutzt, während das Python-Script in der Python-DLL läuft.

Es gibt auch noch ein VCL4Python und FMX4Python, welche die VCL/FMX selbst als DLL mitbringen und was man auch in der Python-EXE nutzen kann, nicht nur Embedded.

0KommaNix 11. Jan 2024 16:15

AW: Wie Embedded-Python und PIP?
 
Danke himitsu!
Da lieferst du mir eine Menge Input :thumb:. Den muss ich erstmal verarbeiten, bevor ich zu chaotisch nachfrage. Bisher habe ich Python nur für mich benutzt und das auch nur getrennt von Delphi-Programmen. Was du schreibst klingen sehr danach, dass ihr leitvolle Erfahrung sammeln musstet. Das macht mich schon Mal vorsichtiger.

Zwischenzeitlich habe ich gelernt, dass venv nicht unabhängig vom global installierten Python ist (ich war so blauäugige das zu glauben :oops:). Nun habe ich gesehen, dass P4D nach einer pyvenv.cfg sucht und darin steht ein absoluter Verweis zum globalen Python.
Bei mein heutigen Versuchen musste ich feststellen, dass wenn ein Skript unter venv in ein Kommandozeilenfenster läuft, es nicht bedeutet, dass es auch von P4D-Demo1 Skript-Memo aus läuft. Bis zum produktiven Einsatz scheint es noch ein weiter Weg zu sein.

himitsu 11. Jan 2024 16:55

AW: Wie Embedded-Python und PIP?
 
Nja, wir hatten ursprünglich Python als "Embedded" mitgeliefert (nur nannte es damals noch niemand so).
Also als ZIP runtergeladen und die EXE (inkl. DLL und Co.) als Unterverzeichnis beim Programm.

Das aktuelle Script wurde als .py-Datei auf die Festplatte geschieben,
dann die Python.exe gestartet, die Datei als Parameter übergeben und dann via ShellExecute/CreateProzess die Ausgabe in eine Datei umgeleitet.
Später der Ausgabestream direkt im CreateProzess übergeben und die Ausgabe in ein unsichtbares TMemo, was dann weiterverwendet wurde und sich beim Dedbuggen anzeigen ließ.

Zuletzt dann Python4Delphi eingeführt, wo das Script als Stream/String im Programm bleibt und auch die Ausgabe in einem Stream/String landet.
Zusätzlich noch ein paar Variablen und Delphi-Funktionen reingegeben, welche sich im Python nutzen lassen.
Auch lässt sich hier intern schöner die Fehlerbehandlung machen. Also vorher ein SyntaxCheck und bei einem Fehler erweiterte Fehlerinfos von der DLL abrufen.

Dann ging es darum auch Fremdcode zu nutzen (bisher vorwiegend nur selbsterstellte PythonScripte)
und da sind wir dann zu PIP übergegangen, da es manuell schnell umständlich wird, mit all den Abhängigkeiten.

Als allerletztes dann noch die Ausgabe im Python auf Unicode umgestellt, denn rein ging es schon als Unicode, aber die Ausgabe sollte eigentlich UTF-8 sein (war aber ANSI).



Bei der EXE war es so, dass die Python-Instanz immer neu ist
und ein Script niemals Auswirkungen auf nachfolgende Scripte hat.

-> Wurde z.B. in einem Script vergessen eine Variable zu initialisieren,
dann was diese Variable in der EXE immer leer,
aber nun in der DLL hatte sie den Wert einer vorhergehenden Ausführung, was das Verhalten manchmal plötzlich veränderte.



Da wir damals es nicht geschafft hatten, die Instanz (Variablen und geladene Module) zurückzusetzen,
wird aktuell nach jeder Ausführung eines Scriptes die Instanz freigegeben und die Python-DLL entladen.
Beim nächsten Laden war somit alles wieder neu/leer.
Das lief bisher gut und auch flott.

Dann versuchte jemand PySide2 und shiboken2 nutzen zu wollen.
Ersteres lädt selbst DLLs und entlädt sich nicht mehr. (Entwickler für Scriptsprachen sind einfach nur faule Schlampen ... siehe meine Signaur)

Diese DLLs haben auch noch eine Referenz auf die python3.dll, welche wiederrum die python38.dll (welche wir geladen haben und nutzen) referenziert, womit sich alles gegenseitig im RAM festkrallt, nachdem wir unsere Referenz auf die python38.dll freigaben und dachten es wäre nun entladen.

Erstmal hatte hier Python4Delphi einen Bug, so dass es in der Finalisazion beim Entladen der einen DLL einen Exception gibt (das ist OK), aber nachfolgend anderes vergessen wird zu finalisieren und freizugeben. (try-finally vergessen)

Dennoch bleibt irgendwas im RAM (auch wenn manuell alle DLLs entladen werden), was dann beim nächsten Laden und der Initialisation zu einer Exception führt und es nicht mehr erlaubt weitere Scripte auszuführen (python4delphi bricht beim Laden und starten des Scripts ab).

Ja, ich kann manuell diese DLLs in der richigen Reihenfolge entladen (testweise für PySide2 gemacht),
aber da der Code universell sein muß, weil ich garnicht weiß, welche Komponenten/PythonModule zukünftig genuzt werden.
Hier hab ich mich nun mühevoll in Windows "gehackt", um an die LadeListen der DLLs zu kommen (inkl. Rehenfolge/Uhrzeit und dem Grund des Ladens, also manuell oder durch automatische Abhängigkeit)
Das wird nun genutzt die FremdDLLs zu finden und zu entladen.

Aber wie gesagt, irgendwo bleibt noch was zurück und knallt nachfolgend. Das hatte ich noch nicht gefunden, drum hängt das Projekt hier erstmal, da Anderes wichtiger.

0KommaNix 12. Jan 2024 18:03

AW: Wie Embedded-Python und PIP?
 
Zitat:

Zitat von himitsu (Beitrag 1531822)
Aber wie gesagt, irgendwo bleibt noch was zurück und knallt nachfolgend. Das hatte ich noch nicht gefunden, drum hängt das Projekt hier erstmal, da Anderes wichtiger.

Das kenn ich nur zu gut. Ich habe gestern Nacht auch erstmal das Handtuch geworfen. Mann darf nicht vergessen wodurch Geld reinkommt und was nur (Lebens-)Zeit und Nerven kostet.
Wir haben ein Kunden, der gerne mein Delphi-Programm durch Python-Skripts an seine Bedürfnisse anpassen möchte. Durch deinen Erfahrungsbericht ist mir bewusst geworden, wie wichtig das "Abstand halten" ist, damit man sich nicht Probleme auf den Tisch zieht, die andere verursachen. Stelle mir nun eine TCP-Client Anbindung an meinen schon vorhandenen TCP-Server vor. Da kann der Kunde draußen spielen bis der Arzt kommt, ohne mein Programm mit in den Abgrund reißen zu können. :-)

himitsu 12. Jan 2024 21:48

AW: Wie Embedded-Python und PIP?
 
Es gibt/gab auch noch die Windows Scripting API.
Das ist eine API, welche man in seinem Programm nutzt, wo sich dann auch andere Sprachen registrieren und somit genutzt werden können.

Diese zu implementieren war nicht das Problem, womit man erstmal JavaScript und VBScript zur Verfügung hat.
Über C# gibt es noch irgendwie eine Implementation für PowerShell und Python (wobei FinalBuilder eher vom Python abrät, laut dem Kommentar in deren ScriptingFenstern)

Die mir bekannte Implementation vom ActiveScripting hatte ich nicht zum Laufen bekommen ... k.A. ob es da noch drin vorhanden ist, seit es die kostenlose Version nicht mehr gibt und in der aktuellen Demo ist es auch nicht drin. (nach der Installation ist keine Registrierung zu finden)

Auch es gibt noch dutzende andere ScriptSprachen, welche dort registriert werden können ... angeblich auch irgendein PascalScript (diesbezüglich hatte ich noch nicht detailiert gesucht ... hab erstmal mit PowerShell, CommandLine/CMD und WSL gespielt und halb fertig)

Ob es diese Speicherlecks des Python auch m WSH gibt, weiß ich nicht ... wie gesagt, die Installation davon funktioniert aktuell nicht mehr
und selbst das Python dort zu registrieren hab ich noch nicht hinbekommen (und falls doch, dann würde ich dort "meine" Speicherlecks natürlich mitbringen).
3 bis 12 oder mehr MB pro Ausführung, klingt nicht viel, aber 100 Mal gemacht, ist ein 32 Bit-Prozess im Arsch.

https://www.delphipraxis.net/214004-...-host-wsh.html


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:26 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz