Während die Popularität von Python zunimmt, treten auch seine Limitationen immer deutlicher zutage: Python-Anwendungen zu schreiben und sie an Personen zu distribuieren, die Python nicht installiert haben, kann ein schwieriges Unterfangen darstellen. Der gängigste Weg, um dieses Problem zu umgehen: Das Programm zusammen mit allen unterstützenden Bibliotheken und Dateien sowie der Python-Laufzeitumgebung zu verpacken. Dafür stehen Tools wie PyInstaller bereit - diese erfordern allerdings jede Menge Kadenz, um korrekt zu funktionieren. Davon abgesehen ist es oft auch möglich, den Quellcode einer Python-Anwendung aus dem resultierenden Paket zu extrahieren. Das wird in einigen Fällen zum Ausschlusskriterium.
Das Third-Party-Projekt nuitka bietet an dieser Stelle eine radikale Lösung an: Es kompiliert ein Python-Programm in eine C-Binärdatei - und zwar nicht, indem es die CPython-Laufzeitumgebung mit dem Bytecode des Programms verpackt, sondern indem es Python-Anweisungen in C übersetzt. Das Ergebnis kann in einer .zip-Datei verteilt oder zusammen mit einem anderen Drittanbieterprodukt in ein Installationsprogramm gepackt werden.
Dabei versucht Nuitka auch, maximale Kompatibilität mit dem Python-Ökosystem aufrechtzuerhalten und die Performance von kompilierten Python-Anwendungen zu verbesssern. Ersteres soll gewährleisten, dass Bibliotheken von Drittanbietern wie NumPy zuverlässig funktionieren. Was die Performance-Zuwächse angeht, gibt es keine Garantien - die Ausbeute variiert stark zwischen den einzelnen Workloads. Im Allgemeinen empfiehlt es sich, Nuitka nicht in erster Linie einzusetzen um die Leistung zu verbessern - vielmehr ist Nuitka als Bündelungslösung zu verstehen.
Nuitka installieren
Nuitka funktioniert mit den Python-Versionen 2.6 bis 2.7 und 3.3 bis 3.10 und kann Binärdateien für
Microsoft Windows,
macOS,
Linux und
FreeBSD/NetBSD kompilieren.
Dabei sollten Sie beachten, dass eine Cross-Kompilierung nicht möglich ist. Sie müssen also die Binärdateien auf der Zielplattform kompilieren. Für jede Plattform benötigen Sie neben der Python-Laufzeitumgebung, auch einen C-Compiler. Unter Microsoft Windows wird Visual Studio 2022 oder höher empfohlen, aber es ist auch möglich, MinGW-w64 C11 (gcc
11.2 oder höher) zu verwenden. Für andere Plattformen können Sie auf gcc
5.1 oder höher, g++
4.4 oder höher, clang
oder clang-cl
auf Windows unter Visual Studio zurückgreifen.
Beachten Sie außerdem: Sie benötigen Python 2.7, wenn Sie Python 3.3 oder Python 3.4 verwenden (die seit langem veraltet sind), weil Abhängigkeiten zwischen den Tools bestehen. Das sollte auch ein Argument dafür sein, wenn möglich die neueste Python-Version zu verwenden.
Optimal ist es, Nuitka in einer virtuellen Umgebung zusammen mit Ihrem Projekt als Entwicklungsabhängigkeit und nicht als Distributionsabhängigkeit zu installieren. Nuitka selbst wird nicht mit Ihrem Projekt gebündelt oder von diesem verwendet, es führt lediglich die Bündelung durch.
Nuitka nutzen
Sobald Sie Nuitka installiert haben, verwenden Sie nuitka
oder python -m nuitka
, um zu beginnen. Das Erste, was Sie mit Nuitka tun sollten: Überprüfen ob die gesamte Toolchain funktioniert - einschließlich Ihres C-Compilers. Um das zu testen, kompilieren Sie ein einfaches "Hello world"-Python-Programm - nennen wir es main.py
:
print ("Hello world")
Wenn Sie ein Python-Programm mit Nuitka kompilieren, übergeben Sie den Namen des Einstiegsmoduls als Parameter an Nuitka, zum Beispiel nuitka main.py
. Wenn Nuitka auf diese Weise aufgerufen wird, nimmt Nuitka main.py
auf und erstellt daraus eine einzige ausführbare Datei.
In diesem Beispiel kompiliert Nuitka nur diese eine Python-Datei zu einem Executable, da wir nur die Funktionalität testen wollen. Es wird weder etwas anderes kompilieren, noch wird es etwas für die Weitergabe bündeln. Diese eine Datei zu kompilieren, sollte jedoch ausreichen, um festzustellen, ob Nuitkas Toolchain korrekt eingerichtet ist. Sobald die Kompilierung abgeschlossen ist, sollten Sie eine ausführbare Binärdatei sehen, die im selben Verzeichnis wie das Python-Programm liegt. Führen Sie diese Datei aus, um sicherzustellen, dass sie funktioniert. Sie können Ihre mit Nuitka kompilierte App auch automatisch ausführen lassen, indem Sie --run
als Kommandozeilen-Flag nutzen.
Ihre erste Testkompilierung mit Nuitka wird wahrscheinlich in wenigen Sekunden abgeschlossen sein. Lassen Sie sich davon nicht beirren. Im Moment kompilieren Sie schließlich nur ein einzelnes Modul, nicht Ihr ganzes Programm. Die Kompilierung einer vollständigen Anwendung mit Nuitka kann einige Minuten (oder länger) in Anspruch nehmen, je nachdem, wie viele Module das Programm verwendet.
Python-Programme mit Nuitka kompilieren
Standardmäßig kompiliert Nuitka nur das von Ihnen angegebene Modul. Wenn Ihr Modul Importe enthält - sei es aus anderen Teilen Ihres Programms, aus der Standardbibliothek oder aus Paketen von Drittanbietern - müssen Sie diese ebenfalls kompilieren.
Beispielhaft betrachten wir eine modifizierte Version des "Hello world"-Programms mit einem angrenzenden Modul namens greet.py
:
def greet(name):
print ("Hallo ", name)
und ein modifiziertes main.py
:
import greet
greet.greet("world")
Um beide Module zu kompilieren, können Sie --follow-imports verwenden:
nuitka --follow-imports main.py
Das sorgt dafür, dass alle im Programm benötigten Importe von den import
-Anweisungen verfolgt und zusammen kompiliert werden.
Mit einer weiteren Option, --nofollow-import-to
, können Sie bestimmte Unterverzeichnisse vom Importprozess ausschließen. Diese Option ist nützlich, um Testsuiten oder Module auszuschließen, von denen Sie wissen, dass sie nie verwendet werden. Sie ermöglicht auch die Angabe eines Platzhalters.
Dynamische Importe einbinden
Ein Problem, mit dem Python-Benutzer oft konfrontiert werden, wenn sie versuchen, eine Python-Anwendung zu verpacken, um sie zu redistribuieren. Die Option --follow-imports
folgt nur Importen, die explizit im Code durch eine import
-Anweisung deklariert sind. Dynamische Importe sind an dieser Stelle nicht inkludiert.
Um das zu umgehen, können Sie --include-plugin-directory
verwenden, um einen oder mehrere Pfade zu Modulen anzugeben, die dynamisch importiert werden. Für ein Verzeichnis mit dem Namen mods
, das dynamisch importierten Code enthält, würden Sie zum Beispiel Folgendes verwenden:
nuitka --follow-imports --include-plugin-directory=mods main.py
Dateien und Verzeichnisse einbeziehen
Wenn Ihr Python-Programm Dateien verwendet, die zur Laufzeit geladen werden, kann Nuitka auch diese nicht automatisch erkennen. Um einzelne Dateien und Verzeichnisse in ein von Nuitka gepacktes Programm einzubinden, verwenden Sie: --include-data-files
und --include-data-dir
.
Mit --include-data-files
können Sie einen Platzhalter für die zu kopierenden Dateien angeben - und wohin sie kopiert werden sollen. Mit --include-data-files=/data/*=data/
werden beispielsweise alle Dateien, die mit dem Platzhalter data/*
übereinstimmen, nach data/
in Ihrem Distributionsverzeichnis kopiert.
--include-data-dir funktioniert in etwa auf die gleiche Weise, nur dass hier kein Platzhalter verwendet wird, sondern nur ein Pfad zum Kopieren und ein Ziel im Distributionsverzeichnis angegeben werden kann. Ein Beispiel: --include-data-dir=/path/to/data=data
würde alles in /path/to/data
in das entsprechende Verzeichnis data
in Ihrem Distributionsverzeichnis kopieren.
Python-Pakete und -Module einbinden
Eine weitere Möglichkeit, Importe zu spezifizieren, ist es, einen Namespace im Python-Stil anstelle eines Dateipfads zu verwenden. Das bewerkstelligen Sie mit --include-package
. Der folgende Befehl würde zum Beispiel mypackage
einschließen, wo immer es sich auf der Festplatte befindet (vorausgesetzt, Python kann es finden):
nuitka --include-package=mypackage main.py
Wenn Pakete ihre eigenen Dateien benötigen, können Sie diese mit der Option --include-package-data inkludieren:
nuitka --include-package=mypackage --include-package-data=mypackage main.py
Dieser Befehl weist Nuitka an, alle Dateien im Paketverzeichnis zu übernehmen, die eigentlich kein Code sind.
Wenn Sie nur ein einzelnes Modul einschließen wollen, können Sie --include-module verwenden:
nuitka --include-module=mypackage.mymodule main.py
Dieser Befehl weist Nuitka an, nur mypackage.mymodule
einzuschließen, aber nichts anderes.
Python-Programme kompilieren und distribuieren
Wenn Sie ein Python-Programm mit Nuitka für die Weiterverbreitung kompilieren möchten, können Sie --standalone
verwenden - das nimmt Ihnen den größten Teil der Arbeit ab: Dieses Kommando folgt automatisch allen Importen und erzeugt einen dist
-Ordner, der die kompilierte ausführbare Datei und alle benötigten Unterstützungsdateien enthält. Um das Programm zu redistribuieren, müssen Sie lediglich dieses Verzeichnis kopieren.
Allerdings sollten Sie nicht erwarten, dass ein mit --standalone
kompiliertes Programm beim ersten Mal funktioniert. Die allgemeine Dynamik von Python-Programmen garantiert, dass Sie einige der anderen oben beschriebenen Optionen verwenden müssen, um sicherzustellen, dass das kompilierte Programm richtig läuft. Wenn Sie zum Beispiel eine GUI-Anwendung haben, die bestimmte Schriftarten benötigt, müssen Sie diese möglicherweise mit --include-data-files
oder --include-data-dir
in Ihre Distribution einfügen.
Wie bereits erwähnt, kann die Kompilierungszeit für eine --standalone
-Anwendung deutlich länger ausfallen als für eine Testkompilierung. Planen Sie die benötigte Zeit ein, um eine vollständig kompilierte Applikation zu testen, sobald Sie eine Vorstellung davon haben, wie lange das dauern wird.
Schließlich bietet Nuitka eine weitere Build-Option: --onefile
. Diejenigen, die mit PyInstaller vertraut sind, werden sich wie zuhause fühlen: Es komprimiert Ihre gesamte Anwendung, einschließlich aller abhängigen Dateien, in eine einzige ausführbare Datei, ohne dass weitere Dateien für die Redistribuierung benötigt werden. Dabei ist es jedoch wichtig zu wissen, dass --onefile
unter Linux und Microsoft Windows unterschiedlich funktioniert:
Unter Linux wird ein virtuelles Dateisystem mit dem Inhalt des Archivs gemountet.
Unter Windows werden die Dateien in ein temporäres Verzeichnis entpackt und von dort aus ausgeführt - und zwar bei jedem Start der Anwendung.
--onefile
unter Windows zu verwenden, kann die Zeit bis zum Programmstart erheblich verlängern.
Dieser Beitrag basiert auf einem Artikel unserer US-Schwesterpublikation InfoWorld. (fm)