Virtuelle Umgebung – ein nützliches Werkzeug in Python

Warum benötigt man virtuelle Umgebungen?

Gehen wir von folgendem Szenario aus: Man hat zwei Projekte: ProjektA und ProjektB, die beide von der gleichen Bibliothek, ProjektC, abhängig sind. Das Problem wird deutlich, wenn wir anfangen, unterschiedliche Versionen von ProjectC zu benötigen.

Vielleicht benötigt ProjectA die Version v1.0.0, während ProjectB die neuere Version v2.0.0 benötigt.
Dies ist ein echtes Problem für Python, da es nicht zwischen den Versionen im site-packages Verzeichnis unterscheiden kann. Sowohl v1.0.0 als auch v2.0.0 würden sich also im selben Verzeichnis mit demselben Namen befinden.

Da Projekte nur nach ihrem Namen gespeichert werden, gibt es keine Unterscheidung zwischen Versionen. Somit müssten beide Projekte, ProjektA und ProjektB, die gleiche Version verwenden, was in vielen Fällen nicht akzeptabel ist.

An dieser Stelle kommen virtuelle Umgebungen und das Werkzeug venv ins Spiel.

Was ist eine virtuelle Umgebung?

Der Hauptzweck von virtuellen Python-Umgebungen besteht darin, eine isolierte Umgebung für Python-Projekte zu schaffen. Das bedeutet, dass jedes Projekt seine eigenen Abhängigkeiten haben kann, unabhängig davon, welche Abhängigkeiten jedes andere Projekt hat.

In unserem kleinen Beispiel oben müssten wir nur eine separate virtuelle Umgebung für ProjektA und ProjektB erstellen, und wir wären startklar. Jede Umgebung könnte dann unabhängig von der anderen von einer beliebigen Version von ProjektC abhängen.

Das Gute daran ist, dass der Anzahl der Umgebungen keine Grenzen gesetzt sind, da es sich nur um Verzeichnisse mit ein paar Skripten handelt. Außerdem lassen sie sich leicht mit den Kommandozeilenwerkzeugen virtualenv oder pyenv erstellen.

Verwendung einer virtuellen Umgebung

Wenn man Python ab Version 3.6 verwendet, dann ist auf den meisten Systemen das Modul venv aus der Standardbibliothek bereits installiert.

Im Projekt- bzw. Arbeitsverzeichnis erstellt man dann eine virtuelle Umgebung mit folgendem Befehl:

$ python3 -m venv .venv

.venv ist hierbei der Verzeichnisname unter dem die Umgebung gespeichert wird. Man kann selbstverständlich auch jeden anderen im System zulässigen Verzeichnisnamen verwenden.
Allerdings vermeidet man mit .venv Verwechslungen mit anderen Verzeichnissen, denn man kann aus dem Namen bereits erkennen, um welches Verzeichnis es sich hier handelt, und wozu es verwendet wird.

Um die virt. Umgebung zu aktivieren, ruft man folgenden Befehl auf:

$ source .venv/bin/activate

Danach ändert sich auch der Prompt, so dass man sicher feststellen kann, ob die Umgebung aktiviert ist oder nicht.

(.venv) $

Möchte man einen aussagekräftigen Prompt, dann kann man einen entsprechenden Parameter bei der Erstellung der virtuellen Umgebung angeben:

$ python3 -m venv .venv --prompt xyz
(xyz) $

Um die Umgebung zu deaktivieren, ruft man den gleichnamigen Befehl auf:

(.venv) $ deactivate

Diese virt. Umgebung enthält nach der Installation und Aktivierung zunächst keine Dritt-Anbieter-Module. Es sind also zunächst die benötigten Module mit pip zu installieren!

Wie funktioniert eine virtuelle Umgebung?

Zu wissen, was unter der Haube vor sich geht, kann für einen Entwickler wichtig sein, insbesondere wenn man Ausführungsumgebungen oder die Auflösung von Abhängigkeiten verstehen muss.

Um zu verstehen, was das „aktivieren“ bewirkt, sehen wir uns zunächst die Speicherorte der verschiedenen ausführbaren Python-Programme an. Zunächst führen wir bei „deaktivierter“ Umgebung Folgendes aus:

$ which python
/usr/bin/python

Jetzt aktivieren wir die Umgebung, und rufen das Kommando erneut auf:

$ source env/bin/activate
(env) $ which python
/Users/thomas/python-projekt/env/bin/python

Nach der Aktivierung der Umgebung erhalten wir nun einen anderen Pfad für die ausführbare Python-Datei, da in einer aktiven Umgebung die Umgebungsvariable $PATH leicht verändert ist.

$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:
$ source env/bin/activate
(env) $ echo $PATH
/Users/thomas/python-projekt/env/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:

Es gibt eigentlich keinen Unterschied zwischen diesen beiden ausführbaren Python-Programmen. Es sind die Verzeichnisse, in denen sie sich befinden.
Wenn Python gestartet wird, schaut es sich den Pfad seiner Binärdatei an. In einer virtuellen Umgebung ist es eigentlich nur eine Kopie oder ein Symlink zur Python-Binärdatei des Systems. Es legt dann die Position von sys.prefix und sys.exec_prefix auf der Grundlage dieser Position fest und lässt den bin-Teil des Pfades weg.
Der Pfad in sys.prefix wird dann zum Auffinden des Site-Packages-Verzeichnisses verwendet, indem der relative Pfad lib/pythonX.X/site-packages/ durchsucht wird, wobei X.X die verwendete Python-Version ist.

In unserem Beispiel befindet sich die Binärdatei unter /Users/thomas/python-projekt/env/bin, was bedeutet, dass sys.prefix /Users/thomas/python-projekt/env ist und das Verzeichnis für die Site-Packages daher /Users/thomas/python-projekt/env/lib/pythonX.X/site-packages ist. Schließlich wird dieser Pfad im Array sys.path gespeichert, das alle Orte enthält, an denen sich ein Paket befinden kann.

Das bedeutet also, sobald eine virtuelle Umgebung aktiviert ist, und mit pip ein Modul installiert wird, wird es unter site-packages der virt. Umgebung gespeichert, und nicht zentral im Systemordner. Damit kann in jeder virt. Umgebung eine andere Version eines bestimmten Moduls vorhanden sein.