Chapitre 216 minDébutant
Environnement virtuel, pip et uv : installer des paquets
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- expliquer pourquoi Debian refuse pip install pour tout le système ;
- créer et activer un environnement virtuel, y installer des paquets ;
- figer et recréer un environnement avec requirements.txt ou uv.lock ;
- écrire un script autonome qui déclare ses dépendances.
3 exercices corrigés1 schéma
Un environnement virtuel est un dossier qui contient son propre interpréteur Python et ses propres paquets, isolés du reste de la machine ; pip installe les paquets dans cet environnement depuis le dépôt PyPI, et uv fait les deux, en beaucoup plus rapide. Ce chapitre explique pourquoi on n'installe plus de paquets « pour tout le système », comment créer et utiliser un environnement virtuel, figer ses dépendances, et comment uv simplifie tout ça, jusqu'au script autonome qui déclare lui-même ses dépendances.
Bibliothèque standard et paquets de PyPI
Python est livré avec une bibliothèque standard très fournie : fichiers, CSV, JSON, dates, expressions régulières, réseau, archives, journalisation, tests. Les chapitres 7 à 12 de ce cours n'utilisent qu'elle. Rien à installer : un import csv suffit.
Pour le reste, il y a PyPI (Python Package Index, pypi.org), le dépôt public des paquets Python : plus de 930 000 projets en novembre 2026. Tu y trouves requests pour appeler une API, pytest pour les tests, Django pour le web. Un paquet s'installe avec pip, l'outil livré avec Python, ou avec uv.
Pourquoi pas « pip install » pour tout le système
Sur Debian 13, tente d'installer un paquet avec le Python du système :
pip install requests
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.
[…]
Ce refus est voulu. Le Python du système appartient à Debian : apt y installe les paquets dont dépendent des outils du système. Si pip remplaçait l'un d'eux par une autre version, un outil pourrait cesser de marcher, et la mise à jour suivante d'apt écraserait peut-être ton installation. Depuis Debian 12 et Ubuntu 23.04, le Python du système se déclare donc « géré de l'extérieur » et pip refuse d'y toucher.
Trois réponses propres :
| Besoin | Solution |
|---|---|
| Un paquet courant pour un script du serveur | Le paquet Debian : sudo apt install python3-requests |
| Un projet avec ses propres dépendances | Un environnement virtuel (ci-dessous) |
| Un outil en ligne de commande écrit en Python | pipx install … ou uv tool install …, qui créent l'environnement pour toi |
Créer un environnement virtuel avec venv
Un environnement virtuel se crée dans le dossier du projet, sous le nom .venv par convention, avec le module venv de la bibliothèque standard :
# Linux et macOS
python3 -m venv .venv
source .venv/bin/activate
# Windows (PowerShell)
python -m venv .venv
.venv\Scripts\Activate.ps1
L'invite du terminal commence alors par (.venv) : les commandes python et pip désignent maintenant l'interpréteur et le pip de l'environnement, quel que soit le système. Installe un paquet, puis liste ce que contient l'environnement :
python -m pip install requests
python -m pip list
Successfully installed certifi-2026.10.23 charset_normalizer-3.5.2 idna-3.20 requests-2.34.2 urllib3-2.8.0
Package Version
------------------ ----------
certifi 2026.10.23
charset-normalizer 3.5.2
idna 3.20
pip 26.2.2
requests 2.34.2
urllib3 2.8.0
requests a amené quatre dépendances : certifi (les autorités de certification pour HTTPS), charset-normalizer, idna et urllib3. Tout est rangé dans .venv : le reste de la machine n'a pas changé. Pour sortir de l'environnement, tape deactivate.
Figer ses dépendances : requirements.txt
Un collègue qui récupère ton script doit pouvoir recréer le même environnement. La commande pip freeze liste les paquets installés avec leur version exacte ; on l'enregistre dans requirements.txt :
python -m pip freeze > requirements.txt
certifi==2026.10.23
charset-normalizer==3.5.2
idna==3.20
requests==2.34.2
urllib3==2.8.0
Sur une autre machine, après avoir créé et activé un environnement : python -m pip install -r requirements.txt. Le fichier requirements.txt se versionne avec le code ; le dossier .venv, jamais : il est propre à chaque machine et se recrée en une commande. Ajoute la ligne .venv/ au .gitignore (le fichier .gitignore).
uv : l'outil moderne
uv est un gestionnaire de paquets et de projets Python, écrit en Rust par la société Astral, libre et gratuit. Il remplace en un seul outil pip, venv et l'installation de versions de Python, et ses auteurs annoncent des installations 10 à 100 fois plus rapides que pip. La version du cours est la 0.13.7, du 3 novembre 2026. Pour l'installer :
# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
# ou
winget install --id=astral-sh.uv -e
# Linux et macOS
curl -LsSf https://astral.sh/uv/install.sh | sh
uv installe aussi des versions de Python, sans toucher à celle du système : uv python install 3.15 sur le serveur Debian 13 de tout à l'heure, puis uv python list pour voir les versions disponibles.
Pour un projet, uv gère tout depuis le fichier pyproject.toml, le fichier standard qui décrit un projet Python :
uv init --no-package inventaire
cd inventaire
uv add requests
uv run main.py
Initialized project `inventaire` at `/home/etudiant/cours-python/inventaire`
Using CPython 3.15.2
Creating virtual environment at: .venv
Resolved 6 packages in 255ms
Prepared 5 packages in 122ms
Installed 5 packages in 3ms
+ certifi==2026.10.23
+ charset-normalizer==3.5.2
+ idna==3.20
+ requests==2.34.2
+ urllib3==2.8.0
Hello from inventaire!
uv init a créé le squelette (pyproject.toml, main.py, README.md et .python-version). uv add a créé l'environnement .venv, installé requests et noté la dépendance dans pyproject.toml :
[project]
name = "inventaire"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.15"
dependencies = [
"requests>=2.34.2",
]
Il a aussi écrit uv.lock, qui fige la version exacte de chaque paquet, dépendances comprises : un collègue qui clone le projet tape uv sync et obtient exactement le même environnement. uv run lance le script dans cet environnement, sans activation. Sans l'option --no-package, uv init prépare depuis la version 0.12 un projet installable, avec le code dans un dossier src/ : utile pour publier un paquet, inutile pour un script.
| Avec pip et venv | Avec uv |
|---|---|
python -m venv .venv | uv venv (ou automatique avec uv add) |
python -m pip install requests | uv add requests dans un projet, uv pip install requests sinon |
python -m pip install -r requirements.txt | uv sync (ou uv pip install -r requirements.txt) |
python -m pip freeze > requirements.txt | uv.lock, écrit et tenu à jour automatiquement |
Activer, puis python main.py | uv run main.py |
pipx install outil | uv tool install outil, ou uvx outil pour le lancer sans l'installer |
Un script autonome qui déclare ses dépendances
Un script d'administration tient souvent dans un seul fichier, qu'on copie d'un serveur à l'autre. Plutôt qu'un requirements.txt à côté, il peut déclarer ses dépendances dans un commentaire spécial en tête du fichier (format standard, défini par la PEP 723). Pars d'un script qui utilise requests :
import requests
reponse = requests.get("https://geo.api.gouv.fr/communes",
params={"codePostal": "71000", "fields": "nom"}, timeout=10)
for commune in reponse.json():
print(commune["nom"])
uv add --script écrit l'en-tête pour toi, puis uv run crée à la volée un environnement avec ce qu'il déclare :
uv add --script communes.py requests
uv run communes.py
# /// script
# requires-python = ">=3.13"
# dependencies = [
# "requests>=2.34.2",
# ]
# ///
import requests
reponse = requests.get("https://geo.api.gouv.fr/communes",
params={"codePostal": "71000", "fields": "nom"}, timeout=10)
for commune in reponse.json():
print(commune["nom"])
Installed 5 packages in 3ms
Mâcon
Sancé
Varennes-lès-Mâcon
Le script interroge l'API officielle du découpage administratif (geo.api.gouv.fr) et affiche les communes du code postal 71000, celui de Mâcon. Au premier lancement, uv installe d'abord requests et ses dépendances dans un environnement réservé au script (la ligne « Installed 5 packages ») ; les fois suivantes, il le réutilise. Le chapitre 13 détaille les appels d'API.
Installer un paquet sans se faire piéger
Installer un paquet, c'est exécuter du code écrit par quelqu'un d'autre, avec tes droits. Des paquets malveillants aux noms proches de paquets connus (reqeusts, python-requests…) apparaissent régulièrement sur PyPI : c'est le typosquatting, une attaque de la chaîne d'approvisionnement logicielle.
- Vérifie le nom exact sur pypi.org : la page du projet, le lien vers son dépôt de code, son historique de versions.
- Fige les versions (
requirements.txt,uv.lock) pour qu'une mise à jour piégée n'entre pas en silence. - Contrôle les vulnérabilités connues de tes dépendances, par exemple avec l'outil
pip-audit(uvx pip-audit). - N'installe pas avec les droits d'administrateur ce qui peut s'installer dans un environnement virtuel.
Le cours Sécurité applicative détaille ces risques (dépendances et SBOM).
Exercices
Exercice 1 sur 3 Dans le bon ordre Réussi À revoir
Sous Windows, tu démarres le projet inventaire dans un dossier vide. Remets ces commandes dans l'ordre et dis à quoi sert chacune :
python -m pip freeze > requirements.txt
python inventaire.py
python -m venv .venv
python -m pip install requests
.venv\Scripts\Activate.ps1
Voir la réponseMasquer la réponse de l'exercice 1
python -m venv .venvcrée l'environnement virtuel dans le dossier.venv..venv\Scripts\Activate.ps1l'active : l'invite commence par(.venv).python -m pip install requestsinstallerequests(et ses dépendances) dans l'environnement.python -m pip freeze > requirements.txtfige les versions installées, pour recréer l'environnement ailleurs.python inventaire.pylance le script avec l'interpréteur de l'environnement.
Les étapes 4 et 5 peuvent s'inverser ; la 4 se refait à chaque nouveau paquet. Avec uv, le même travail tient en deux commandes : uv init --no-package inventaire puis uv add requests, et uv run inventaire.py pour lancer.
Tu avais trouvé ?
Exercice 2 sur 3 Installé, mais introuvable Réussi À revoir
Tu as tapé pip install requests sans erreur. Pourtant, ton script s'arrête sur :
ModuleNotFoundError: No module named 'requests'
Donne trois causes possibles et la vérification à faire pour chacune.
Voir la réponseMasquer la réponse de l'exercice 2
Le paquet est installé, mais pas dans l'interpréteur qui exécute le script.
- L'environnement n'est pas activé dans le terminal qui lance le script :
pipa installé ailleurs. Vérifie que l'invite commence par(.venv), ou lance avec.venv/bin/python script.py. pipetpythonne sont pas le même Python (plusieurs versions installées) :python -m pip install requestsrègle le problème, car le paquet va dans l'interpréteur qu'on appelle.- Visual Studio Code utilise un autre interpréteur que celui du terminal :
Ctrl+Maj+P, Python: Select Interpreter, choisis celui du dossier.venv.
Pour savoir quel interpréteur tourne, ajoute temporairement import sys; print(sys.executable) en tête du script.
Tu avais trouvé ?
Exercice 3 sur 3 Un script planifié sur un serveur Réussi À revoir
Chez Sorbier Logistique, Antoine veut lancer chaque nuit, sur le serveur Debian 13 srv-journaux, un script controle.py qui utilise requests. Il est rangé dans /opt/controle/. Propose deux façons propres de lui fournir requests, et la ligne que la tâche planifiée doit exécuter dans chaque cas. Pourquoi pas sudo pip install requests ?
Voir la réponseMasquer la réponse de l'exercice 3
Solution 1, le paquet Debian. sudo apt install python3-requests, puis la tâche lance /usr/bin/python3 /opt/controle/controle.py. La version vient de Debian et se met à jour avec le système.
Solution 2, un environnement virtuel dédié. python3 -m venv /opt/controle/.venv, puis /opt/controle/.venv/bin/python -m pip install -r /opt/controle/requirements.txt. La tâche lance /opt/controle/.venv/bin/python /opt/controle/controle.py : appeler directement l'interpréteur de l'environnement suffit, sans activation. Le script a la version de requests qu'il attend, figée dans requirements.txt.
(Une troisième voie : déclarer requests dans l'en-tête du script et le lancer avec uv run, si uv est installé pour le compte qui exécute la tâche.)
sudo pip install est refusé sur Debian 13 (externally-managed-environment) : forcer avec --break-system-packages risque de casser des outils du système qui dépendent d'autres versions des mêmes paquets. Et installer en root du code venu de PyPI lui donne tous les droits sur le serveur. La planification elle-même (cron ou un timer systemd) est détaillée au chapitre 10.
Tu avais trouvé ?