Wer damit anfängt mit der beliebten Sprache Python zu programmieren, wird früher oder später bei der Paketverwaltung Pip landen. Als Entwickler kann man seinen Code über Pip anderen anbieten, die ihn mit einem Befehl installieren und in ihren eigenen Programmen nutzen können. Dank der Arbeit anderer kann man dadurch Funktionen einfacher und schneller einbauen. Man findet dort hunderttausende Pakete, von kleineren Helfern über API-Schnittstellen bis hin zu mächtigen Frameworks mit dutzenden Funktionen.
Beispiel: Für einen Slack-Bot gibt es das Paket slack_sdk, für einen Telegram-Bot pyTelegramBotAPI und so weiter. Möchtest du so ein Paket installieren, wird es standardmäßig im Home-Verzeichnis des angemeldeten Benutzers angelegt, bei der Nutzung als root (sudo) sogar global.
$ pip3 install slack_sdk
$ pip3 list -v | grep slack
slack-sdk 3.15.1 /home/pi/.local/lib/python3.7/site-packages pip
HINWEIS: Pip ist standardmäßig nicht vorinstalliert und muss zunächst händisch installiert werden:
sudo apt install python3-pip
Anfangs ist das kein Problem, sondern sogar effizient. Schließlich wird ein Paket wie z.B. slack_sdk nur einmal installiert, egal ob man es in einem oder fünf Projekten verwendet. Spätestens wenn man mehr als ein Python-Projekt hat, kann es zu Konflikten kommen: In Projekt A wird z.B. das Paket slack_sdk in Version 3.0 verlangt, Projekt B nutzt das gleiche Paket in Version 3.15. Wir haben aber nur eine Version installiert: Entweder die neuere, womit Projekt A Probleme bekommen kann. Oder die ältere, in der möglicherweise neue Funktionen fehlen, die Projekt B benötigt.
Dies ist ein grundsätzliches Problem, egal ob wir ein Paket für Slack, Telegram oder etwas völlig anderes installieren. Man bedenke, dass Pip mittlerweile hunderttausende Pakete umfasst. Zumal dieses Beispiel nur von direkten Konflikten ausgeht. Es kann auch zu indirekten Konflikten kommen, wenn zwei verschiedene Pakete wiederum beide von einem dritten Paket in unterschiedlichen Versionen abhängt.
Die Lösung: Eine virtuelle Python-Umgebung
Das Problem sind also die globalen Pakete, die entweder für einen Benutzer oder sogar für das gesamte System installiert werden. Hier kommt das Werkzeug Virtualenv ins Spiel: Es erzeugt eine virtuelle Umgebung pro Projekt, in das die Pakete installiert werden – statt global. Dadurch können sich Abhängigkeiten verschiedener Projekte nicht mehr in die Quere kommen.
Es gibt zwei Arten von virtuellen Umgebungen: venv ist in Python3 integriert, enthält aber nur einen Teil der Funktionen und ist langsamer. Ich würde daher virtualenv verwenden, dies müssen wir zunächst installieren:
sudo apt install python3-virtualenv
# Manjaro
pamac install python-virtualenv
Legen wir nun eine erste beispielhafte virtuelle Umgebung für ein Projekt im Home-Verzeichnis an. Sie muss anschließend aktiviert werden:
# Alternativ: python3 -m virtualenv
$ virtualenv ./hello_world_virtualenv
created virtual environment CPython3.9.2.final.0-64 in 474ms
creator CPython3Posix(dest=/home/pi/hello_world_virtualenv, clear=False, no_vcs_ignore=False, global=False)
seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/pi/.local/share/virtualenv)
added seed packages: pip==20.3.4, pkg_resources==0.0.0, setuptools==44.1.1, wheel==0.34.2
activators BashActivator,CShellActivator,FishActivator,PowerShellActivator,PythonActivator,XonshActivator
pi@pi:~ $ source ./hello_world_virtualenv/bin/activate
(hello_world_virtualenv) pi@pi:~ $
Der Name der Umgebung erscheint dadurch links im Promt. So seht ihr sofort, dass ihr euch in einer virtuellen Umgebung befindet und in welcher. Die installierten Pakete lassen sich mit pip3 list anzeigen:
(hello_world_virtualenv) pi@pi:~ $ pip3 list
Package Version
------------- -------
pip 20.3.4
pkg-resources 0.0.0
setuptools 44.1.1
wheel 0.34.2
Das sind Standardpakete von Python. Nun können wir testweise ein eigenes Paket installieren, z.B. pyTelegramBotAPI:
(hello_world_virtualenv) pi@pi:~ $ pip3 install pyTelegramBotAPI
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting pyTelegramBotAPI
[...]
(hello_world_virtualenv) pi@pi:~ $ pip3 list
Package Version
------------------ ---------
certifi 2021.10.8
charset-normalizer 2.0.12
idna 3.3
pip 20.3.4
pkg-resources 0.0.0
pyTelegramBotAPI 4.4.0
requests 2.27.1
setuptools 44.1.1
urllib3 1.26.8
wheel 0.34.2
Anschließend taucht es in der Liste auf. Die anderen neu dazugekommenen Pakete stammen von Abhängigkeiten. Beispielsweise nutzt das Telegram-Paket wiederum das Paket requests, um HTTP-Anfragen durchzuführen. Um zu bestätigen, dass diese Pakete nur in unserer virtuellen Umgebung installiert sind, verlassen wir sie einfach einmal. Dazu einfach den Befehl deactivate eingeben und der Umgebungsname am Anfang verschwindet:
(hello_world_virtualenv) pi@pi:~ $ deactivate
pi@pi:~ $ pip3 list
Package Version
------------------ ---------
appdirs 1.4.4
certifi 2020.6.20
chardet 4.0.0
colorzero 1.1
distlib 0.3.1
distro 1.5.0
filelock 3.0.12
gpiozero 1.6.2
idna 2.10
importlib-metadata 1.6.0
more-itertools 4.2.0
pip 20.3.4
python-apt 2.2.1
requests 2.25.1
RPi.GPIO 0.7.0
setuptools 52.0.0
six 1.16.0
spidev 3.5
ssh-import-id 5.10
urllib3 1.26.5
virtualenv 20.4.0+ds
wheel 0.34.2
zipp 1.0.0
Hier sind auf dem Raspberry Pi standardmäßig ein paar Pakete installiert, dies hat aber nichts mit unserer virtuellen Umgebung zutun. Das soeben installierte Telegram-Paket. Erst wenn wir zurück in die zuvor erstellte virtuelle Umgebung wechseln, taucht es dort auf:
pi@pi:~ $ pip3 list | grep -i telegram
pi@pi:~ $
pi@pi:~ $ source ./hello_world_virtualenv/bin/activate
(hello_world_virtualenv) pi@pi:~ $ pip3 list | grep -i telegram
pyTelegramBotAPI 4.4.0
(hello_world_virtualenv) pi@pi:~ $