Hinweis: Dieser Artikel richtet sich an Entwickler:innen mit wenig bis mittlerer Erfahrung.
Python
Gerade bei Einsteigern erfreut sich die Programmiersprache Python einer andauernden Beliebtheit. Dafür gibt es viele Gründe:
- einfach zu erlernen
- gut dokumentiert
- viele Beispiele
- deckt fast alle Anwendungsfälle ab
- erweiterbar durch viele Bibliotheken
- die Syntax ist gut lesbar
- schnelle turn-around-Zeiten, dank Interpreter
- läuft auf allen grossen Betriebssystemen (cross platform)
- schnelles Prototyping
Im Tiobe-Index belegt Python seit Jahren den ersten Platz. Alle oben genannten Vorteile beziehen sich auf die Sprache selbst, nicht jedoch auf die Aspekte Paketbau und Installation. Deshalb geht es in diesem Beitrag nicht um die Sprache selbst, sondern um eben diese Aspekte.
Paketbau
Im Linux-Universum gibt es viele Paketformate. Das beginnt bei den distributionsspezifischen Formaten, wie deb, rpm, pkgbuild, usw. Weiter geht es mit den Container-Paketen Flatpak, Snap und AppImage. Selbstverständlich kann man seine Python-Anwendungen in diesen Formaten bauen, aber es gibt noch mehr. Nämlich das Kauderwelsch der Python-Welt. Da gibt es Eggs und Wheels, die Setup-Tools, UV, PyPi, die pydevtools, Poetry, pip, pipx und vieles mehr. Zugegeben, diese Liste enthält einen Mischmasch von Formaten, Werkzeugen und Repositories.
Sucht doch einmal nach "Python Packaging". Ich garantiere dafür, dass ihr nach fünf Minuten Lesen den Verstand verliert.
Die Python-Organisation schreibt dazu (im Walde pfeifend):
Das Thema Paketierung in Python hat den Ruf, nicht ganz einfach zu sein. Dieser Eindruck ist vor allem ein Nebenprodukt der Vielseitigkeit von Python. Sobald man die natürlichen Grenzen der einzelnen Paketierungslösungen versteht, wird einem klar, dass diese vielfältige Landschaft ein geringer Preis ist, den Python-Programmierer dafür zahlen, dass sie eine der ausgewogensten und flexibelsten Sprachen nutzen, die es gibt.
Jetzt geht es los!
Ich nehme an, dass ihr bereits einige Python-Skripte geschrieben habt, die auf eurem lokalen Rechner einwandfrei laufen. Nun besteht der Wunsch, die selbst geschriebenen Anwendungen mit der Community zu teilen, und mit anderen zusammen daran arbeiten zu können.
Dafür benötigt ihr die Source-Code-Versionsverwaltung Git. Der Client-Teil ist auf eurem Computer wahrscheinlich vorhanden. Falls nicht, dann installiert ihr Git im Paketmanager eurer Distribution (hier steht, wie es geht). Für die Server-Seite von Git, empfehle ich Codeberg. Klickt euch dort einen kostenlosen Account. Der deutsche Verein freut sich über Spenden.
In diesem Artikel erkläre ich nicht, wie man Python-Programme schreibt. Ich erkläre auch nicht, wie Git funktioniert. Wer mehr darüber erfahren möchte, findet viele Quellen im Internet.
Idealerweise habt ihr ein eigenes Python-Skript, das nicht zu simpel und nicht zu kompliziert ist. Es sollte so aussehen:
- Alles in einem Verzeichnis
- Mindestens eine *.py-Datei mit einer main-Funktion, die Modul-fähig ist:
def main():...if __name__ == "__main__":main() - Mindestens eine Abhängigkeit von einer externen Python-Library, z. B.: pyradios
- Mindestens eine Abhängigkeit von einem anderen Paket, z. B.: mpv
Hier seht ihr ein Beispiel aus meinem Projekt Station:
Bisher interessiert euch dort nur eine Datei: station.py. Das ist der Programmcode. Alles andere sind Zutaten:
- data und img enthalten Daten- und Bilddateien. img ist uninteressant; auf data komme ich noch zurück.
- .gitignore enthält Angaben zu den Dateien, die nicht auf den Git-Server kopiert werden sollen.
- CHANGELOG, LICENSE und README enthalten Informationen für die Community. Nett, aber nicht lebenswichtig.
- Die Datei pyproject.toml ist der Kernteil dieses Artikels. Dort spielt die Musik, wenn es um den Paketbau und die Installation geht.
pyproject.toml
Diese Datei ist der Dreh- und Angelpunkt für den Paketbau eures Python-Projekts. Hier seht ihr ein kommentiertes Beispiel aus meinem Projekt Station.
[project]
name = "station"
dynamic = ["version"]
description = "A CLI terminal radio player"
license = "GPL-3.0-or-later"
readme = "README.md"
requires-python = ">=3.9"
dependencies = ["mpv>=1.0.8", "pyradios>=2.1.1"]
[project.urls]
Homepage = "https://codeberg.org/ralfhersel/station"
Repository = "https://codeberg.org/ralfhersel/station"
Changelog = "https://codeberg.org/ralfhersel/station/src/branch/main/CHANGELOG.md"
[project.scripts]
station = "station:main"
[build-system]
requires = ["setuptools>=80", "setuptools-scm[simple]>=9.2"]
build-backend = "setuptools.build_meta"
[tool.setuptools]
packages = ["data"]
py-modules = ["station"]
[tool.setuptools.package-data]
data = ["*.csv"]
Darin seht ihr mehrere Kapitel in eckigen Klammern.
Im Kapitel [project] sind die meisten Einträge selbsterklärend. Die könnt ihr so übernehmen und eure eigenen Texte eintragen. Bei dependencies tragt ihr alle Abhängigkeiten ein; sowohl Python-Libraries, die nicht zum Standard-Umfang von Python gehören, als auch alle anderen Pakete, die euer Programm benötigt. Dazu gehören auch die Versionen der Pakete, die euer Programm mindestens benötigt. Falls ihr das nicht wisst, dann tragt einfach die Versionsnummer ein, die auf eurem Rechner läuft. Aber Achtung: Falls ihr eine Rolling-Distro verwendet, wird die aktuelle Version dazu führen, dass das Programm auf den meisten Stable-Distros nicht läuft, weil dort ältere Versionen installiert sind. Tipp: Tragt die Version ein, die mit Debian-Stable geliefert wird.
Damit ihr bei [project.urls] etwas eintragen könnt, benötigt ihr ein Git-Repository. Die Einträge sind selbsterklärend. Ohne ein Git-Repo, kann niemand euren Kram installieren, aktualisieren und mitarbeiten.
Das Kapitel [project.scripts] ist nötig, damit klar ist, welches Python-Skript gestartet werden soll. Angenommen, eure Haupt-Python-Datei heisst meine_anwendung.py, dann steht in diesem Kapitel:
meine_anwendung = "meine_anwendung:main"
Dieses Python-Skript muss natürlich eine main-Funktion enthalten. Weiter oben seht ihr ein Beispiel.
In den folgenden Kapiteln steckt die Magie, an der Prof.P., Elfchen und ich, seit einiger Zeit frickeln. Das Kapitel [build-system] könnt ihr so übernehmen. Nehmt zur Kenntnis, dass die Python Setuptools zum Einsatz kommen.
Setuptools ist eine Sammlung von Erweiterungen für die Python-Bibliothek „distutils“, die es Entwicklern ermöglichen, Python-Pakete einfacher zu erstellen, darunter auch solche, die Abhängigkeiten zu anderen Paketen und C/C++-Erweiterungsmodulen haben.
Wenn ihr nichts kapiert, macht das nichts. Nehmt es so hin und lest weiter.
Im Kapitel [tool.setuptools] wird es wichtig. Hier seht ihr zwei Einträge. py-modules = ["meine_anwendung"] ist zwingend und zeigt auf eure Haupt-Python-Datei (die mit der main-Funktion). Der zweite Eintrag ist der Grund, warum wir lange nach einer Lösung gesucht haben. Wenn ihr Zusatzdateien in eurem Python-Projekt habt, können die nicht im Root-Verzeichnis der Anwendung liegen. Solche Zusatzdateien können z. B. Daten- oder Konfigurationsdateien sein. Schaut mal auf den Screenshot weiter oben. Dort seht ihr ein Unterverzeichnis data. Darin befindet sich die Daten-Datei station.csv. Der zweite Eintrag teilt dem Build-System (also Setuptools) mit, dass sich der zusätzliche Kram in einem bestimmten Unterverzeichnis befindet. In meinem Beispiel ist es das Verzeichnis data.
packages = ["data"]
Im letzten Kapitel [tool.setuptools.package-data] könnt ihr die Zusatzdateien ausdrücklich benennen:
data = ["*.csv"]
Im Beispiel werden nur CSV-Dateien berücksichtigt. Vielleicht kann man dieses Kapitel weglassen oder generisch angeben (data = ["*"]). Doch das habe ich nicht getestet.
Lokal testen
Auch ohne eigenes Git-Repository könnt ihr den Spass auf eurer Maschine testen. Eine Voraussetzung ist die Installation von pipx über den Paketmanager eurer Linux-Distribution. Zum Testen führt ihr im Code-Verzeichnis diesen Befehl aus:
pipx install --force --include-deps .
Achtet bitte auf den Punkt am Ende; der gehört dazu. Er steht dafür, das aktuelle Verzeichnis, in dem man sich gerade befindet, zu verwenden. Der Parameter --force erspart euch die Deinstallation nach der ersten Testrunde. Nach dem Ausführen seht ihr im Terminal, ob es funktioniert hat oder nicht. Eine erfolgreiche Installation sieht so aus:
Danach könnt ihr meine_anwendung (im Beispiel: station) aus jedem Verzeichnis heraus starten. Vom grafischen Desktop könnt ihr die Python-Anwendung starten, falls ihr eine passende *.desktop-Datei dafür erstellt, was ich hier nicht erkläre.
Venv - Virtuelle Umgebung für Python
Vermutlich habt ihr bemerkt, dass pipx für euch eine virtuelle Umgebung für die Python-Anwendung aufbaut. Das ist ein isolierter Raum für die Installation und das Ausführen eurer Anwendung. Damit wird sichergestellt, dass euer Murks nicht das generelle Python-System der Distribution durcheinanderbringt.
Man kann auf die Idee kommen, sein super-duper Python-Skript auch ohne Paketierung und Installierbarkeit an die Leute zu verteilen. Ja, das habe ich jahrelang so gemacht. Einfach die Python-Dateien über ein Git-Repo bereitstellen. Damit schliesst man potenzielle Anwender:innen aus, die nicht in der Lage sind, die nötigen Abhängigkeiten von Hand zu installieren.
Selbstverständlich kann man auch bei dieser Variante ein Venv aufbauen, womit fast alle Anwender:innen überfordert sind. Dem halte ich entgegen, dass man für ein Kindergarten-Programm kein Venv benötigt. Ich habe mir in den vergangenen 10 Jahren noch nie die Python-Basis zerschossen, weil ich kein Venv eingerichtet hatte. Wie dem auch sei, mit pipx müsst ihr euch darüber keine Gedanken machen.
Deployen
Wie zuvor erwähnt, erkläre ich hier nicht, wie Git funktioniert. Daher nur in aller Kürze:
git add -A
git commit -m "Hurra, mein erster Git-Commit"
git push
Damit landet der aktuelle Code in eurem Git-Repository.
Installieren und ausführen
Damit andere Leute euer Programm installieren können, verwenden sie diesen Aufruf im Terminal:
pipx install --include-deps git+https://codeberg.org/dein_name/dein_programm
Danach landet die Anwendung in diesem Verzeichnis:
~/.local/share/pipx/venvs/dein_programm
Deine Anwendung kann jetzt im Terminal von überall aus gestartet werden, und zwar mit dem Programmnamen ohne die .py Endung.
Fazit
Puh, dieser Artikel ist länger geworden, als ich vermutet habe. Ich glaube, dass pipx mit setuptools die einfachste Methode ist, um Python-Anwendungen zu paketieren und zu installieren. Wie zu Beginn, weise ich erneut darauf hin, dass sich dieser Beitrag an Einsteiger:innen richtet. Deshalb bitte ich darum, die Kommentare in diese Richtung zu lenken.
Mein besonderer Dank geht an Prof.P. und an Elfchen für die fruchtbare Unterstützung. Ohne Elfchen hätten wir nicht herausgefunden, wie man die Zusatzdateien installiert.
Alle Python-Entwickler:innen mit mässiger Erfahrung können hoffentlich aus diesem Beitrag Nutzen ziehen.
Titelbild: https://www.pexels.com/de-de/foto/person-hande-festhalten-halten-4440774/
Quellen:
https://labex.io/pythoncheatsheet/de/cheatsheet/packaging
https://www.pypa.io/en/latest/
https://packaging.python.org/en/latest/


