Virtuelle Umgebungen in Python: wann du eine brauchst und wann nicht
Die Regel ist einfacher, als die Werkzeugvielfalt vermuten lässt: Sobald zwei Projekte oder das Betriebssystem denselben Interpreter benutzen, brauchst du eine Trennung. Alles andere ist eine Frage des Werkzeugs.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Ein Paketupdate für ein Projekt legt ein anderes still
Der typische Verlauf braucht keine Fahrlässigkeit, nur zwei Projekte. Im ersten läuft eine Bibliothek in einer älteren Fassung, weil ein Abhängigkeitsbaum sie so festlegt. Im zweiten brauchst du eine neuere Funktion und installierst das Paket mit pip. Weil beide Projekte denselben Interpreter benutzen, gibt es nur eine Fassung, und das ist ab sofort die neue. Das erste Projekt startet weiterhin, es fällt erst dort auseinander, wo es eine entfernte Funktion aufruft, und das kann eine Woche später und in einem selten benutzten Pfad sein.
Auf Linux kommt ein zweiter Betroffener dazu, der in der Diskussion meistens fehlt: das Betriebssystem. Paketverwaltung, Netzwerkwerkzeuge und Teile der Desktop-Umgebung sind selbst Python-Programme und benutzen denselben Interpreter unter /usr/bin. Eine Installation mit erhöhten Rechten in genau dieses Verzeichnis kann dir die Paketverwaltung zerlegen, und der Weg zurück führt dann nicht über pip, sondern über die Wiederherstellung des Systems. Genau deshalb weisen aktuelle Distributionen eine solche Installation inzwischen von sich aus ab.
Der dritte Fall ist der teuerste, weil er erst im Betrieb auffällt. Auf dem Entwicklerrechner sind über die Jahre Pakete gelandet, die niemand mehr bewusst installiert hat, und der Code benutzt eines davon, ohne dass es in der Abhängigkeitsliste steht. Lokal läuft alles. Auf dem CI-Server, in einem frischen Container oder beim neuen Kollegen fehlt das Paket, und der Import bricht ab. Eine eigene Umgebung pro Projekt macht diese Lücke sofort sichtbar, statt sie jahrelang zu verdecken.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Die Meldungen, an denen es im Alltag hängt
Sortiert nach dem, was du auf dem Bildschirm siehst. Zu jedem Fall steht, welche Eigenschaft der Umgebung dahintersteckt und welcher Handgriff ihn dauerhaft erledigt, statt ihn nur zu übergehen.
Symptom
pip bricht mit der Meldung externally-managed-environment ab, obwohl du auf dem Rechner alle Rechte hast.
Ursache
Die Distribution hat neben der Standardbibliothek eine Datei mit dem Namen EXTERNALLY-MANAGED abgelegt. Damit teilt sie pip mit, dass diese Installation der Paketverwaltung des Systems gehört. Der Mechanismus ist in PEP 668 festgelegt und auf aktuellen Debian- und Fedora-Systemen sowie bei Homebrew der Normalfall. Die Meldung ist kein Fehler, sondern eine Schutzmaßnahme.
Lösung
Leg mit python3 -m venv .venv eine eigene Umgebung an und installiere darin. Geht es um ein Werkzeug für die Kommandozeile und nicht um eine Bibliothek, nimm pipx oder uv tool install. Den Schalter --break-system-packages gibt es, er hebelt aber genau die Sperre aus, die dir gerade die Paketverwaltung gerettet hat, und gehört deshalb höchstens in ein Wegwerf-Image.
Symptom
Nach einem Update für Projekt B startet Projekt A nicht mehr, oder es bricht an einer Stelle ab, die seit Monaten unverändert ist.
Ursache
Beide Projekte benutzen denselben Interpreter, und damit gibt es jedes Paket nur einmal. Die Auflösung der Abhängigkeiten hat beim zweiten Projekt eine Fassung installiert, die das erste nicht verträgt. Bei pip fällt das nicht einmal auf, wenn beide Projekte gar nicht gemeinsam aufgelöst wurden.
Lösung
Eine Umgebung pro Projekt, ohne Ausnahme. Bestehende Konflikte machst du mit pip check sichtbar, das prüft die installierten Fassungen gegen die deklarierten Anforderungen. Danach legst du für jedes Projekt eine frische Umgebung an und installierst aus der Abhängigkeitsliste, nicht aus dem Gedächtnis.
Symptom
Die Umgebung ist aktiviert, trotzdem landet die Installation woanders oder ein gerade installiertes Paket lässt sich nicht importieren.
Ursache
Das Aktivierungsskript verändert nur den Suchpfad der Shell. Wenn davor ein anderes Verzeichnis liegt, etwa aus einer Werkzeugverwaltung oder von Homebrew, oder wenn du eine Shell öffnest, die das Skript nicht ausgeführt hat, zeigt der Aufruf pip weiterhin auf eine andere Installation. In Editoren tritt derselbe Fall auf, wenn dort ein anderer Interpreter eingestellt ist als in deinem Terminal.
Lösung
Ruf im Zweifel python -m pip auf statt pip, dann installiert garantiert der Interpreter, den du auch startest. Zur Kontrolle vergleichst du in einer Python-Sitzung sys.prefix mit sys.base_prefix: Sind beide gleich, bist du nicht in einer Umgebung. Im Editor stellst du den Interpreter ausdrücklich auf die Datei im Verzeichnis .venv ein, statt dich auf die automatische Erkennung zu verlassen.
Symptom
Ein Verzeichnis mit einer Umgebung wurde verschoben, umbenannt oder auf einen anderen Rechner kopiert, und danach starten die Skripte darin nicht mehr.
Ursache
In der Umgebung stehen absolute Pfade: in der Datei pyvenv.cfg, in den erzeugten Startskripten und in deren erster Zeile. Der Interpreter selbst liegt normalerweise gar nicht darin, er ist nur verknüpft. Eine venv ist deshalb ein Ergebnis und kein Artefakt, das man weitergibt.
Lösung
Trag das Verzeichnis in die .gitignore ein und leg es überall neu an. Was du weitergibst, sind die Abhängigkeitsliste in der pyproject.toml und das Lockfile. Auf einem neuen Rechner ist die Umgebung damit in Sekunden wiederhergestellt, und zwar mit den Fassungen, die dort auch gelten sollen.
Symptom
In einem Notebook fehlt ein Paket, das du kurz vorher in der aktivierten Umgebung installiert hast.
Ursache
Ein Notebook läuft nicht in der Shell, sondern in einem Kernel, und der Kernel merkt sich beim Registrieren einen festen Pfad zum Interpreter. Wenn du Jupyter global installiert hast, zeigt der Standardkernel auf diese globale Installation und nicht auf deine Projektumgebung, egal was in der Shell aktiv ist.
Lösung
Installiere ipykernel in der Projektumgebung und registriere von dort aus einen eigenen Kernel mit sprechendem Namen. Danach wählst du im Notebook diesen Kernel aus. Zur Kontrolle gibst du in einer Zelle sys.executable aus, das ist der einzige verlässliche Beleg dafür, welcher Interpreter tatsächlich rechnet.
Symptom
In einer conda-Umgebung wurde zusätzlich mit pip installiert, und seitdem lässt sich die Umgebung nicht mehr sauber aktualisieren oder aus der Beschreibung neu aufbauen.
Ursache
Beide Werkzeuge verwalten dasselbe Verzeichnis, führen aber getrennt Buch. conda kennt die mit pip installierten Dateien nicht und überschreibt sie beim nächsten Auflösen, und umgekehrt sieht pip die von conda verwalteten Fassungen nicht als das an, was sie sind. Bei Paketen mit kompilierten Anteilen kommen unterschiedliche Systembibliotheken dazu.
Lösung
Entscheide dich pro Umgebung für ein Werkzeug. Wenn du conda für die Systembibliotheken brauchst, etwa bei Geodaten oder bei einer bestimmten Rechenbibliothek, dann installiere alles über conda und nimm pip nur für Pakete, die es dort nicht gibt, und zwar zuletzt und dokumentiert. Für reine Python-Projekte ist venv mit einem Lockfile der einfachere Weg.
Fünf Fragen, und du weißt, was du anlegen musst
- 01 Liegt mehr als ein Python-Projekt auf dem Rechner? Dann braucht jedes seine eigene Umgebung.
- 02 Benutzt das Betriebssystem denselben Interpreter? Dann fass ihn gar nicht erst an.
- 03 Soll das Ergebnis ein Werkzeug für die Kommandozeile sein? Dann nimm pipx oder uv tool.
- 04 Läuft die Anwendung allein in einem Container? Dann ist die Trennung schon vorhanden.
- 05 Sollen in zwei Wochen dieselben Fassungen darin liegen? Dann fehlt dir noch ein Lockfile.
Was du nach dieser Seite in fünf Minuten entscheidest
Die Frage ist nicht, ob virtuelle Umgebungen gut sind, sondern wo genau die Trennung herkommt. Die folgenden sechs Punkte beantworten das für jeden Fall, der im Alltag auftritt, und sie sagen dir auch, wann du dir den Schritt sparen kannst.
Auf dem Entwicklerrechner immer eine eigene Umgebung
Sobald zwei Projekte auf einem Rechner liegen, ist die Frage entschieden. Ein Verzeichnis .venv im Projekt, angelegt mit python3 -m venv .venv oder mit uv venv, kostet Sekunden und nimmt dir die gesamte Klasse an Versionskonflikten ab. Der Name .venv ist keine Vorschrift, aber alle gängigen Entwicklungsumgebungen finden ihn von selbst.
Kommandozeilenwerkzeuge gehören nicht ins Projekt
Werkzeuge, die du überall aufrufen willst, also etwa ein Formatter, ein Linter oder ein Migrationswerkzeug, haben in der Projektumgebung nichts verloren. Dafür gibt es pipx und uv tool install. Beide legen im Hintergrund je eine eigene Umgebung pro Werkzeug an und hängen nur die ausführbare Datei in den Suchpfad.
Im Container ist die Trennung schon da
Ein Image, das genau eine Anwendung enthält, ist bereits die Isolation, für die du sonst eine venv anlegst. Eine zusätzliche Umgebung darin ist kein Fehler, aber sie bringt nur dann etwas, wenn das Basisabbild Python-Pakete des Systems mitbringt oder wenn du in einem mehrstufigen Build ein fertiges Verzeichnis in das schlanke Endabbild kopieren willst.
Einzelskripte brauchen keine Umgebung mehr
Für ein Skript, das drei Abhängigkeiten hat und einmal die Woche läuft, kannst du die Abhängigkeiten nach dem in PEP 723 festgelegten Format als Kommentarblock in die Datei schreiben. Werkzeuge wie uv lesen diesen Block, legen die passende Umgebung im Hintergrund an und werfen sie wieder weg. Die Datei bleibt eine einzelne Datei, die du weitergeben kannst.
Umgebung und Reproduzierbarkeit sind zwei verschiedene Dinge
Eine venv sorgt dafür, dass sich Projekte nicht gegenseitig stören. Sie sorgt nicht dafür, dass in zwei Wochen dieselben Fassungen darin liegen. Dafür brauchst du ein Lockfile, also uv.lock, poetry.lock oder eine pylock.toml nach PEP 751. Wer nur die Umgebung einführt und das Lockfile weglässt, hat die Hälfte des Problems gelöst.
Eine Umgebung ist keine Sicherheitsgrenze
Die Aktivierung setzt Umgebungsvariablen, im Wesentlichen den Suchpfad. Der Code darin läuft mit deinen Rechten, liest deine Dateien und öffnet Netzwerkverbindungen wie jedes andere Programm. Wenn du unbekannten Code prüfen willst, ist das ein Fall für einen Container oder eine eigene virtuelle Maschine, nicht für eine venv.
KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Was eine virtuelle Umgebung technisch überhaupt ist
Hinter dem Begriff steckt weniger, als der Name vermuten lässt. Eine virtuelle Umgebung ist ein Verzeichnis mit drei relevanten Bestandteilen: einer Datei pyvenv.cfg, die auf den zugrunde liegenden Interpreter zeigt, einem Verzeichnis für ausführbare Dateien und einem eigenen site-packages, in dem die Pakete landen. Der Interpreter wird dabei in der Regel nicht kopiert, sondern nur verknüpft. Deshalb ist eine frische Umgebung in Sekunden angelegt und belegt fast keinen Platz.
Die Aktivierung ist ebenfalls unspektakulär. Das Skript setzt den Suchpfad so, dass das Verzeichnis der Umgebung vorne steht, und merkt sich den vorherigen Zustand für das Verlassen. Mehr passiert nicht. Genau deshalb funktioniert es auch ohne Aktivierung, wenn du den Interpreter mit vollem Pfad aufrufst, und genau deshalb hilft die Aktivierung nichts in einem Prozess, der sie nie gesehen hat, etwa in einem Dienst mit eigener Startdatei.
Was eine Umgebung nicht leistet, ist ebenso wichtig. Sie trennt Python-Pakete, keine Systembibliotheken. Ein Paket mit kompilierten Anteilen bringt sein eigenes Kompilat als Wheel mit, greift zur Laufzeit aber weiterhin auf Bibliotheken des Systems zu. Sie trennt auch keine Rechte und keine Dateizugriffe. Und sie legt die Python-Fassung fest, die beim Anlegen benutzt wurde, weshalb ein Sprung von einer Nebenversion auf die nächste immer bedeutet, die Umgebung neu anzulegen.
Die Fälle, in denen du dir den Schritt sparen kannst
Der erste Fall ist der Container mit genau einer Anwendung. Das Image enthält nichts außer dieser Anwendung und ihren Abhängigkeiten, es gibt kein zweites Projekt, das gestört werden könnte, und beim nächsten Start ist alles wieder wie beim Bauen. Eine zusätzliche Umgebung darin bringt vor allem dann etwas, wenn das Basis-Image ein vom System verwaltetes Python mitbringt oder wenn du in einem mehrstufigen Build ein fertiges Verzeichnis in ein schlankes End-Image kopieren willst. Beide Gründe sind gute Gründe, aber sie sind Gründe und keine Gewohnheit.
Der zweite Fall ist der CI-Job, der auf einem frischen Runner startet und danach weggeworfen wird. Dort ist die Isolation die CI-Umgebung selbst. Trotzdem legen viele Vorlagen eine Umgebung an, und das aus einem nachvollziehbaren Grund: Sie sorgt dafür, dass Zwischenspeicher zwischen Läufen berechenbar bleiben und dass der Job auch auf einem Runner funktioniert, der nicht frisch ist. Wenn du gemeinsam genutzte Runner betreibst, ist die Umgebung dort deshalb keine Zeremonie, sondern Absicherung.
Der dritte Fall ist das Einzelskript. Seit PEP 723 gibt es ein festgelegtes Format, mit dem ein Skript seine Abhängigkeiten und die verlangte Python-Fassung in einem Kommentarblock am Anfang selbst mitbringt. Ein Werkzeug wie uv liest den Block, richtet im Hintergrund eine passende Umgebung ein und startet das Skript darin. Für Automatisierung, kleine Auswertungen und Werkzeuge, die per Mail weitergereicht werden, ist das der kürzere Weg, weil das Ergebnis eine einzige Datei bleibt.
Welches Werkzeug die Umgebung anlegt und warum das kaum eine Rolle spielt
Das Modul venv gehört zur Standardbibliothek und ist überall da, wo Python ist. Auf schlanken Linux-Images muss es allerdings gelegentlich als eigenes Systempaket nachinstalliert werden, was der einzige regelmäßige Stolperstein ist. Das ältere virtualenv aus der Gemeinschaft kann zusätzlich Umgebungen für andere Python-Fassungen anlegen und ist beim wiederholten Erzeugen schneller, weshalb es in Werkzeugketten wie tox weiterhin verbreitet ist.
uv venv erzeugt dasselbe Ergebnis deutlich schneller und kann die passende Python-Fassung bei Bedarf gleich mitbringen, statt sie auf dem System vorauszusetzen. Poetry und Hatch legen die Umgebung von sich aus an, sobald du ein Projekt einrichtest, und verstecken den Schritt damit weitgehend. conda schließlich ist ein eigenes Ökosystem, das nicht nur Python-Pakete verwaltet, sondern auch die Bibliotheken darunter, und das ist in der Datenanalyse und bei Geodaten oft genau der Punkt.
Für die Frage, ob du eine Umgebung brauchst, ist das Werkzeug zweitrangig. Wichtiger ist, dass im Team alle dasselbe benutzen. Zwei Wege nebeneinander bedeuten zwei Anleitungen für neue Kollegen, zwei Pfade im CI und regelmäßig die Frage, warum es beim einen läuft und beim anderen nicht. Diese Festlegung gehört in die Datei, die ohnehin jeder liest, und sie ist eine Entscheidung des Teams und keine des einzelnen Rechners.
Trennung ist noch keine Wiederholbarkeit
Eine frische Umgebung und ein pip install aus einer Liste ohne feste Fassungen ergeben an zwei Tagen zwei verschiedene Ergebnisse, weil in der Zwischenzeit irgendwo im Abhängigkeitsbaum eine neue Version erschienen ist. Die Umgebung war dabei nie das Problem, sie hat nur dafür gesorgt, dass der Unterschied nicht auch noch andere Projekte erwischt. Für gleiche Ergebnisse brauchst du ein Lockfile mit allen Fassungen einschließlich der indirekten Abhängigkeiten.
Seit PEP 751 gibt es dafür ein werkzeugübergreifendes Format mit dem Dateinamen pylock.toml. uv kann sein eigenes Lockfile in dieses Format ausgeben, Poetry unterstützt den Export über ein Zusatzmodul, und pip liest solche Dateien inzwischen als Eingabe für die Installation. Das Format ist damit noch nicht überall der Normalfall, aber es ist der erste gemeinsame Nenner, den es in diesem Bereich je gab.
Ein zweiter Punkt gehört daneben: Das Lockfile hält die Fassungen fest, nicht die Python-Version. Wenn der CI-Server eine andere Nebenversion benutzt als dein Rechner, bekommst du bei Paketen mit kompilierten Anteilen andere Wheels und gelegentlich anderes Verhalten. Trag deshalb requires-python in der pyproject.toml ein und leg die Fassung im CI ausdrücklich fest, statt sie dem Runner zu überlassen.
Dazu passende Kurse
Wenn im Team jeder seine eigene Vorstellung von Umgebungen und Paketen mitbringt, sind Python-Kurse, die bei der Werkzeugkette anfangen der kürzeste Weg zu einer gemeinsamen Grundlage.
Wer zum ersten Mal ein Projekt aufsetzt und die Begriffe hinter Interpreter, Paket und Abhängigkeit noch sortieren muss, findet den passenden Anfang in Kurse für den Einstieg in die Softwareentwicklung .
Wie sicher bist du beim Thema wirklich?
Lesen fühlt sich schnell nach Können an. Ein kurzer Test zeigt dir, was davon schon sitzt und wo sich ein Kurs lohnt. Kostenlos, ohne Anmeldung, mit einer Erklärung zu jeder Antwort.
Sehr umfangreicher Überblick über Zusammenhänge und Anwendungsbereiche. Sehr zu empfehlen.
Effektives, gemeinsames Erarbeiten der Aufgaben mit vielen praktischen Tipps.
Sehr guter Trainer und gute Aufbereitung der Inhalte und auch eingehen auf Fragen der Beteiligten.
Häufige Fragen
Muss die Umgebung .venv heißen und im Projekt liegen?
Gehört das Verzeichnis der Umgebung ins Repository?
Brauche ich in einem Docker-Image trotzdem eine venv?
Was ist der Unterschied zwischen venv und conda?
Muss ich die Umgebung wirklich jedes Mal aktivieren?
Deine Ansprechpartner
Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.
Yves Hoppe
Weiterbildung & Beratung
Hilft dir, aus dem Programmierung-Programm den passenden Kurs für deinen Stand zu finden.
Norbert Jansen
Beratung & Inhouse
Plant mit dir Inhouse-Trainings, die auf eure Abläufe und euren Datenbestand zugeschnitten sind.
Die Werkzeugkette einmal gemeinsam aufsetzen
In den Python-Kursen bei cmt richtest du Umgebung, Abhängigkeiten und Lockfile an einem eigenen Projekt ein, statt die Reihenfolge später im Betrieb zu lernen. Die Kurse laufen vor Ort und Live-Online.
Passt dazu
- uv, pip oder poetry: womit ihr eure Python-Abhängigkeiten verwaltet
- pytest läuft lokal, scheitert in der CI: die häufigsten Ursachen
- Type Hints in Python: was sie bringen und wo mypy anschlägt
- Python ohne GIL: was der freie Threading-Modus für euren Code ändert