Python Entwicklung

15. Feb. 2024

Python ist eine weit verbreitete und leicht zu erlernende Programmiersprache, die dank zahlreicher Erweiterungen (in Form von Modulen und Paketen) breit Anwendung findet. Doch da Python und dessen Erweiterungen ständig weiterentwickelt werden, kann es beim Teilen von Code zu Problemen kommen. Benutzt z.B. ein Kollege eine andere Version von Python oder einer installierten Erweiterung, so besteht die Möglichkeit, dass der Code, der auf dem eigenen Computer einwandfrei funktionert, beim Kollegen Fehler produziert. Arbeitet man zudem an unetrschiedlichen Projekten gleichzeitig, können sich verschiedene Erweiterungen gegenseitig stören, z.B. wenn Modul A für Projekt 1 ein Modul B mit Version 1.3 benötigt, bei Projekt 2 jedoch Modul B mit Version 2.1 zum Einsatz kommen soll. Werden alle Erweiterungen mit pip installiert, kann dies die System-Version von Python "vollmüllen" und das Arbeiten an verschiedenen Projekten erschweren. Um diesem Problem Abhilfe zu verschaffen, bedient man sich einer Python Versionsverwaltung (mit pyenv) und einer virtuellen Umgebung (mit virtualenv). Wie diese Tools unter macOS installiert werden, wird in diesem Beitrag beschrieben.

Pyenv

Pyenv (Github) ist ein kleines Programm, das Python-Versionen verwaltet. Es wird entweder duch einen Script wie folgt installiert:

curl https://pyenv.run | bash

oder mittels Homebrew (meine bevorzugte Weise, Programme zu installieren) wie folgt:

brew update
brew install pyenv

Nach der Installation muss man noch den Shell-Start-Script aktualisieren, sodass Pyenv und die dafür benötigten Umgebungsvariablen geladen werden. Bei macOS wird zsh als Shell eingesetzt, wesshalb die Datei .zshrc im Benutzerordner (also ~/.zshrc) wie folgt ergänzt werden muss:

echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc

Wie die Shell-Start-Scrips für andere Betriebssysteme und Shellumgebungen geändert werden müssen, wird im Github-Repository von Pyenv gut beschrieben.

Startet man das Terminal neu (oder läd die Änderungen mit source ~/.zshrc), kann man den Befehl pyenv eingeben und z.B. mit dem Argument --version prüfen, ob die aktuellste Version von Pyenv richtig installiert wurde:

$ pyenv --version
pyenv 2.3.35

Um nun eine bestimmte Python-Version zu installieren, kann man sich mit dem folgenden Befehl, zuerst alle zur Verfügung stehenden Versionen auflisten lassen:

pyenv install --list

In meinem Fall waren es 743 verschiedene Versionen, die installiert werden können. Um z.B. Python 3.12.1 zu installieren, geht dies mit dem folgenden Befehl:

pyenv install 3.12.1

Nach erfolgreicher Installation lässt sich mit pyenv versions prüfen, welche Versionen nun zur Auswahl stehen:

$ pyenv versions
* system
  3.12.1

Man sieht, die neue Version ist installiert, aber prüft gibt man python --version ins Terminal ein, sieht man, dass die System-Version benutzt wird. Dies kann geändert werden, mit den Befehlen pyenv local und pyenv global. Das Schlüsselwort local weist Pyenv an, in dem derzeitigen Ordner eine bestimmte Python-Version zu benutzen, während global eine Python-Version allgemein festsetzt.

Für Testzwecke können wir im Ordner ~/Developer/python-test den folgenden Befehl ausführen:

pyenv local 3.12.1

und danach die Versionen abfragen:

$ pyenv versions
  system
* 3.12.1 (set by /Users/maxi/Developer/python-test/.python-version)

Durch das Sternchen erkennt man, dass nun die zuletzt installierte Version von Python ausgewählt wurde. Dies liegt an der versteckten Datei .python-versions im Ordner python-test. Prüft man nun die Python-Version, sieht man, dass diese Version nun auch engesetzt wird:

$ python --version
Python 3.12.1

Der Folgende Befehl zeugt an, wo sich der Python-Interpreter befindet:

$ which python
/Users/maxi/.pyenv/shims/python

Man sieht, dass Python in einem von Pyenv erstellten Ordner gespechert ist. Werden nun mehrere Versionen von Python wie oben installiert, speichert Pyenv alle Versionen in diesem Ordner und durch Angabe einer Version mittel local oder global wird auf die entsprechende Version verwiesen.

Das bedeute aber auch, dass zwei Projekten die dieselbe Python-Version bebutzen, auch dieselben Erweiterungen (also Module und Pakete) bereitgestellt werden. Installiert man eine Erweiterung mittels pip, so wird diese Erweiterung im Ordner .pyenv im Benutzerordner (also ~/.pyenv) für die derzeit ausgewählte Python-Version installiert und steht immer dann zur Verfügung, wenn die entsprechende Python-Version eingesetzt wird.

Zwar kann man mit dem Schlüsselwort uninstall die Python-Version entfernen und sie mit install sauber neu installieren. Doch das ist umständlich, wenn zwischen verschiedenen Projekten gewechselt und dafür Python immer erneut installiert werden muss. So kommen wir zum zweiten Tool: virtualenv

Virtualenv

Eine virtuelle Umgebung, die mit virtualenv erstellt wird, kann man sich wie eine lokale Kopie der derzeit verwendeten Python-Version vorstellen, die alle Erweiterungen lokal verwaltet und somit die ursprüngliche Python-Installation unverändert lässt. Das Modul muss zuerst installiert werden (und zwar für jede frisch installierte Python-Version) mit dem folgenden Befehl:

pip install virtualenv

Und nun kann man z.B. im Ordner ~/Developer/python-test eine virtuelle Umgebung wie folgt anlegen:

python -m virtualenv .venv

Dadurch wird ein versteckter Ordner namens .venv erzeugt, der die derzeit aktivierte Python-Version lokal bereitstellt. Aktiviert man diese virtuelle Umgebung mit:

source .venv/bin/activate

so kann man nun mithilfe von pip Erweiterungen lokal installieren, also ohne dass die ursprüngliche Python-Version im .pyenv Ordner oder denn die System-Version, verändert (oder "vollgemüllt") wird. Alle Erweiterungen werden ausschließlich im .venv Ordner lokal installiert.

Möchte man die virtuelle Umgebung verlassen, so wird sie wie folgt deaktiviert:

deactivate

Man kann den .venv Ordner ohne Bedenken löschen und neu erstellen und ist somit von Projekt zu Projekt flexibel hinsichtlich der Installation verschiedener Erweiterungen.

Letzter Tipp

Hat man mehrere Erweiterungen in einer virtuellen Umgebung installiert und möchte seinen Code teilen, so empfiehlt sich die folgende Herangehensweise. Mit dem folgenden Befehl werden alle Module und Pakete in einer Datei so angeben:

pip freeze > requirements.txt

dass ein Kollege (nachdem er die virtuelle Umgebung mit der entsprechenden Python-Version aufgesetzt und aktiviert hat) alle Abhängigkeiten schnell installieren und den Code ausführen kann:

pip install -r requirements.txt

Das bedeutet, man muss nicht den .venv Ordner mit allen Erweiterungen teilen (insb. nicht auf Github). Stattdessen braucht nur nur angeben, welche Python-Version einzusetzen ist (z.B. in der Readme.md Datei oder durch die .python-version Datei) und man muss die Abhängigkeiten durch die requirements.txt Datei angeben. Somit ist alles bereitgestellt, um den Code ausführen zu können.