Table of Contents
Szenario
Du entwickelst ein kleines Python-Skript, das externe Abhängigkeiten verwendet (z. B. requests, pandas oder andere Pakete aus PyPI). Möchtest du dieses Skript weitergeben, müssen Empfänger die erforderlichen Dependencies manuell installieren. Das ist nicht besonders benutzerfreundlich.
Ein naheliegender Ansatz wäre es, das Skript mit pyinstaller in ein ausführbares Binary zu verpacken – allerdings ist das für kleine Tools oft überdimensioniert.
Idee: „Bootstrapping“ à la Münchhausen
Angelehnt an die Geschichte des Baron Münchhausen, der sich selbst am eigenen Schopf aus dem Sumpf zog: Auch in Python kann sich ein Skript „selbst helfen“. Das Skript prüft beim Start, ob benötigte Pakete installiert sind. Falls nicht, installiert es diese zur Laufzeit mittels subprocess und versucht anschließend erneut den Import.
Erster Wurf
Hier ein Code-Beispiel mit dem Paket dotenv
import subprocess
import sys
import importlib
try:
import dotenv
print("dotenv already installed.")
except ImportError:
print("dotenv not installed: trying to install")
result = subprocess.run([sys.executable, "-m", "pip", "install", "python-dotenv"])
if result.returncode == 0:
try:
importlib.import_module("dotenv")
print("dotenv is now available.")
except ImportError:
print("dotenv still cannot be imported after installation.")
else:
print("dotenv could not be installed")
Funktion für mehrere Pakete
Ein häufiges Problem besteht darin, dass der Name eines Pakets beim Import nicht mit dem Namen übereinstimmt, unter dem es via pip installiert wird.
Im obigen Code siehst du bereits dass dotenv mit import dotenv eingebunden wird, aber über pip install python-dotenv installiert werden muss.
Dieses Mismatch lässt sich elegant lösen, indem man ein Dictionary definiert, das die Zuordnung zwischen Importnamen und pip-Paketnamen enthält.
Anschließend kann man über dieses Dictionary iterieren, den Import jedes Moduls versuchen und – falls erforderlich – das zugehörige pip-Paket automatisch nachinstallieren.
import subprocess
import sys
import importlib
dict_of_packages = {
"dotenv": "python-dotenv",
"xlrd": "xlrd"
}
def ensure_packages(dict_of_packages):
for package_name, package_pip_name in dict_of_packages.items():
try:
importlib.import_module(package_name)
print(f"{package_name} already installed.")
except ImportError:
print(f"{package_name} not installed: trying to install")
result = subprocess.run([sys.executable, "-m", "pip", "install", package_pip_name])
if result.returncode == 0:
try:
importlib.import_module(package_name)
print(f"{package_name} is now available.")
except ImportError:
print(f"{package_name} still cannot be imported after installation.")
else:
print(f"{package_name} could not be installed")
Fazit
Dieser Ansatz funktioniert gut in kontrollierten Umgebungen (bspw. venv, Skripte für Kollegen, Schulungen).
Bei Deployment auf Produktivsystemen sollte Package-Management über requirements.txt und pip install -r bevorzugt werden.
Für komplexere Setups oder GUI-Tools sind Build-Systeme wie setuptools, pipx, shiv, pex oder pyinstaller besser geeignet.