backend / python
Modules et packaging : venv, pip, pyproject.toml
Explication
Ce que vous allez apprendre
- Organiser du code en modules (
fichier.py) et packages (dossier avec__init__.py) - Comprendre à quoi sert
if __name__ == "__main__":et pourquoi il est presque partout - Créer un environnement virtuel avec
venvpour isoler les dépendances d'un projet - Installer, figer et rejouer des dépendances avec
pipetrequirements.txt - Décrire un projet avec
pyproject.toml, le standard moderne qui remplacesetup.py
Dans quel contexte ?
Un développeur travaille sur deux projets sur la même machine : l'un nécessite django==4.2 pour un ancien client, l'autre django==5.0 pour un projet récent. Installer les paquets globalement avec pip install django provoquerait un conflit impossible à résoudre. Créer un environnement virtuel distinct pour chaque projet (python3 -m venv .venv) isole complètement leurs dépendances, comme si chaque projet avait sa propre installation Python.
Passer d'un script à un projet
Un seul fichier .py suffit pour un script rapide, mais un projet réel a besoin d'organiser son code en plusieurs fichiers cohérents. Un module, c'est simplement un fichier .py qu'on peut importer ailleurs. Un package, c'est un dossier de modules regroupés, reconnaissable historiquement par la présence d'un fichier __init__.py.
Pourquoi le test sur name est partout
Un même fichier peut être exécuté directement (python script.py) ou importé depuis un autre fichier (import script). Dans le second cas, on ne veut généralement pas que le code de démonstration du script s'exécute automatiquement. La condition if __name__ == "__main__": distingue les deux situations : __name__ vaut "__main__" seulement lors d'une exécution directe, jamais lors d'un import.
Bonne pratique
Placez toujours le code de démonstration ou de test rapide d'un module sous if __name__ == "__main__":. Un autre développeur qui importera votre module plus tard ne verra jamais ce code s'exécuter par accident.
Isoler les dépendances, projet par projet
Installer des paquets globalement sur sa machine finit toujours par provoquer des conflits de versions entre projets différents. Un environnement virtuel (venv) crée une installation Python isolée et propre à un projet, dans laquelle pip installe les dépendances sans polluer le reste du système. C'est une pratique non négociable dès qu'on travaille sur plus d'un projet Python.
Le standard qui a unifié le packaging
Pendant longtemps, chaque projet Python avait son propre setup.py avec une syntaxe parfois fantaisiste. pyproject.toml est devenu le standard moderne qui décrit un projet (métadonnées, dépendances, outils de build) dans un format déclaratif unique, indépendant de l'outil utilisé pour construire le package.
| Commande | Effet |
|---|---|
python3 -m venv .venv | Crée un environnement virtuel isolé |
pip install requests | Installe un paquet dans l'environnement actif |
pip freeze > requirements.txt | Fige les versions exactes installées |
pip install -r requirements.txt | Réinstalle exactement les mêmes versions ailleurs |
Cette leçon change de registre par rapport aux précédentes : après le langage lui-même, on aborde l'écosystème qui entoure tout projet Python sérieux, thème qui reviendra aux leçons sur les tests et le packaging PyPI.
Commandes & code
Modules et packaging
Organiser du code en modules/packages, et gérer les dépendances proprement.
# --- Modules : un fichier .py est un module ---
# fichier : mathutils.py
def carre(x):
return x ** 2
PI = 3.14159
# fichier : main.py
import mathutils
print(mathutils.carre(4))
print(mathutils.PI)
from mathutils import carre, PI # import cible
from mathutils import carre as square # alias
import mathutils as mu # alias du module
# --- Packages : un dossier avec __init__.py ---
# structure :
# mon_package/
# __init__.py
# core.py
# utils/
# __init__.py
# helpers.py
# mon_package/__init__.py peut exposer une API publique simplifiee :
# from .core import fonction_principale
# from .utils.helpers import aide
# import relatif (uniquement a l'interieur d'un package)
# dans mon_package/utils/helpers.py :
# from ..core import fonction_principale # remonte d'un niveau
# __name__ == "__main__" : distinguer execution directe vs import
def main():
print("Programme principal")
if __name__ == "__main__":
# ce bloc ne s'execute QUE si le fichier est lance directement,
# pas quand il est importe depuis un autre module
main()
# __all__ : controle ce que "from module import *" expose
__all__ = ["fonction_publique", "AUTRE_CONSTANTE"]# --- Environnements virtuels : isoler les dependances par projet ---
python3 -m venv .venv # creer un environnement virtuel
# Activation
source .venv/bin/activate # Linux / macOS
.venv\Scripts\activate # Windows (PowerShell/cmd)
deactivate # quitter l'environnement virtuel
# --- pip : gestionnaire de paquets ---
pip install requests # installer un paquet
pip install requests==2.31.0 # version precise
pip install "requests>=2.28,<3.0" # plage de versions
pip install -e . # installation editable du projet courant
pip uninstall requests
pip list # paquets installes
pip freeze > requirements.txt # figer les versions exactes
pip install -r requirements.txt # installer depuis un fichier# --- pyproject.toml : standard moderne (PEP 517/518/621) pour decrire un projet ---
[build-system]
requires = ["setuptools>=68.0"]
build-backend = "setuptools.build_meta"
[project]
name = "mon-projet"
version = "1.0.0"
description = "Un projet Python exemplaire"
readme = "README.md"
requires-python = ">=3.11"
license = {text = "MIT"}
authors = [{name = "Alice", email = "alice@example.com"}]
dependencies = [
"requests>=2.28",
"pydantic>=2.0",
]
[project.optional-dependencies]
dev = ["pytest>=7.0", "ruff>=0.1", "mypy>=1.5"]
[project.scripts]
mon-cli = "mon_projet.cli:main" # cree une commande "mon-cli" apres installation
[tool.ruff]
line-length = 100
[tool.pytest.ini_options]
testpaths = ["tests"]# Outils modernes recommandes en 2024+
pip install uv # installateur/resolveur ultra-rapide (remplace pip)
uv venv # cree un venv
uv pip install -r requirements.txt # installation rapide
# Structure de projet standard recommandee
# mon_projet/
# pyproject.toml
# src/
# mon_projet/
# __init__.py
# core.py
# tests/
# test_core.py
# README.mdRésumé
- Un module est un fichier
.py, un package est un dossier avec__init__.py. if __name__ == "__main__"distingue un script exécuté directement d'un module importé.- Toujours travailler dans un environnement virtuel (
venv) pour isoler les dépendances par projet. pyproject.tomlest le standard moderne unifié (remplacesetup.py/setup.cfg).
Exercices pratiques
Mission : sauver deux projets qui s'entre-détruisent sur la même machine
Objectif : Résoudre un conflit de versions de dépendances entre deux projets avec des environnements virtuels, puis sécuriser un module contre une exécution accidentelle de son code de démonstration.
Contexte
Un développeur travaille sur deux projets sur la même machine : l'un a besoin de django==4.2, l'autre de django==5.0. Il a installé les deux versions globalement avec pip install, et désormais un seul des deux projets fonctionne à la fois, selon la dernière installation effectuée.
Tu dois isoler les deux projets avec des environnements virtuels distincts, puis corriger un module qui exécute son code de démonstration à chaque fois qu'il est importé ailleurs, à cause d'un test __name__ manquant.