Aller au contenu

Chapitre 12 — Les outils du développeur : venv, pip et Git

⏱️ Durée estimée : 5 à 7 heures (cours + exercices + mini-projet)

📋 Prérequis : avoir terminé le Chapitre 11 — POO avancée, héritage et typage et savoir utiliser un terminal (Chapitre 00).

🎯 Objectifs — À la fin de ce chapitre, tu sauras…

  • créer, activer et désactiver un environnement virtuel (venv) sous Windows, macOS et Linux ;
  • installer, lister, désinstaller des paquets avec pip et partager tes dépendances avec un fichier requirements.txt ;
  • situer les outils modernes uv et pyproject.toml ;
  • suivre l'historique de ton code avec Git : init, status, add, commit, log, diff ;
  • travailler avec des branches, les fusionner et résoudre un conflit simple ;
  • publier ton projet sur GitHub et le récupérer ailleurs avec clone, push et pull.

Cours

Jusqu'ici, tu as écrit du code Python « tout seul dans ton coin » : un fichier, tu l'exécutes, c'est tout. Les vrais projets ont deux besoins supplémentaires :

  1. Utiliser du code écrit par d'autres (des bibliothèques comme requests, pandas, flask…) sans casser ton ordinateur ni tes autres projets. → C'est le rôle de venv et pip.
  2. Garder l'historique de ton travail, pouvoir revenir en arrière, essayer des idées sans risque et collaborer. → C'est le rôle de Git et GitHub.

Ces outils ne sont pas du Python à proprement parler, mais aucun développeur ne travaille sans eux. Prends le temps de bien les comprendre : tu t'en serviras dans tous tes projets à venir.

1. Pourquoi des environnements virtuels ?

Imagine une cuisine partagée par toute une colocation. Chacun range ses ingrédients dans le même placard. Un jour, Léa remplace le sel par du sel au céleri pour sa recette… et le gâteau de Tom a un goût étrange. 🤢

C'est exactement ce qui arrive quand tu installes toutes tes bibliothèques dans le Python « global » de ton ordinateur :

  • le projet A a besoin de pandas version 1.x ;
  • le projet B a besoin de pandas version 2.x ;
  • impossible d'avoir les deux en même temps dans le même placard !

Un environnement virtuel (virtual environment, abrégé venv) est un placard privé par projet : un dossier qui contient sa propre copie de l'interpréteur Python et ses propres bibliothèques. Ce que tu installes dedans ne touche ni les autres projets, ni le Python du système.

mon_projet/
├── .venv/              ← le placard privé (ne se partage pas, ne se commit pas)
│   ├── bin/ ou Scripts/  ← python, pip… de ce projet
│   └── lib/              ← les bibliothèques installées pour ce projet
├── main.py
└── requirements.txt    ← la liste de courses (elle, on la partage)

💡 Règle d'or : un projet = un venv. On crée le venv dans le dossier du projet, et on l'appelle par convention .venv (avec un point, ce qui le cache sur macOS/Linux et le rend facile à reconnaître).

2. Créer, activer et désactiver un venv

2.1 Créer le venv

Ouvre un terminal dans le dossier de ton projet (dans VS Code : Terminal → Nouveau terminal), puis :

# macOS / Linux
python3 -m venv .venv
# Windows (PowerShell ou cmd)
py -m venv .venv

Décortiquons : python3 -m venv signifie « exécute le module venv de Python », et .venv est le nom du dossier à créer. L'opération prend quelques secondes ; un dossier .venv apparaît.

2.2 Activer le venv

Créer le venv ne suffit pas : il faut l'activer pour que les commandes python et pip du terminal pointent vers le placard privé.

# macOS / Linux (bash, zsh)
source .venv/bin/activate
# Windows PowerShell
.venv\Scripts\Activate.ps1
:: Windows "Invite de commandes" (cmd.exe)
.venv\Scripts\activate.bat

Une fois activé, le nom du venv s'affiche au début de l'invite du terminal :

(.venv) camille@portable:~/mon_projet$

C'est ton signal visuel : « je suis dans le placard privé ». À partir de là, sur tous les systèmes, tu peux simplement taper python et pip (plus besoin de python3 ou py).

⚠️ Windows : « l'exécution de scripts est désactivée sur ce système » La première fois, PowerShell peut refuser d'exécuter Activate.ps1. Lance une seule fois cette commande, puis réessaie :

Set-ExecutionPolicy -Scope CurrentUser RemoteSigned

Elle autorise les scripts locaux pour ton utilisateur uniquement.

2.3 Vérifier qu'on est bien dans le venv

python -c "import sys; print(sys.prefix)"

Si le chemin affiché se termine par .venv, c'est gagné. Autre méthode :

# macOS / Linux
which python
# Windows PowerShell
Get-Command python

2.4 Désactiver le venv

deactivate

Cette commande est la même partout. Le (.venv) disparaît de l'invite. Fermer le terminal désactive aussi le venv.

2.5 VS Code et le venv

VS Code détecte généralement le dossier .venv tout seul et te propose de l'utiliser. Sinon : Ctrl+Maj+P (ou Cmd+Maj+P sur Mac) → Python: Select Interpreter → choisis celui qui contient .venv. Ensuite, chaque nouveau terminal ouvert dans VS Code active automatiquement le venv.

2.6 Supprimer un venv

Un venv est jetable : si tu as un doute, supprime le dossier .venv et recrée-le. Comme tes dépendances sont listées dans requirements.txt (voir plus bas), tu ne perds rien.

# macOS / Linux
rm -rf .venv
# Windows PowerShell
Remove-Item -Recurse -Force .venv

3. pip : le gestionnaire de paquets

pip (Package Installer for Python) télécharge des bibliothèques depuis PyPI (Python Package Index, pypi.org), l'immense catalogue public de paquets Python : plus de 500 000 projets !

💡 Analogie : PyPI est le supermarché, pip est ton chariot, le venv est ton placard.

Toutes les commandes ci-dessous s'exécutent venv activé.

3.1 Installer un paquet

pip install requests

pip télécharge requests et ses dépendances (les bibliothèques dont requests a lui-même besoin). Tu peux ensuite l'utiliser :

import requests

reponse = requests.get("https://api.github.com")
print(reponse.status_code)   # 200 si tout va bien

Variantes utiles :

pip install requests==2.32.3      # une version précise
pip install "requests>=2.30"      # au moins cette version (guillemets : à cause du >)
pip install --upgrade requests    # mettre à jour vers la dernière version
pip install rich pandas           # plusieurs paquets d'un coup

💡 Astuce robuste : python -m pip install requests est équivalent à pip install requests, mais garantit que tu utilises le pip du Python actuel. En cas de doute (surtout sous Windows), préfère cette forme.

3.2 Lister et inspecter

pip list                # tous les paquets installés, présentation lisible
pip show requests       # détails : version, auteur, dépendances, emplacement…

Exemple de sortie de pip list dans un venv tout neuf où l'on a installé requests :

Package            Version
------------------ ---------
certifi            ...
charset-normalizer ...
idna               ...
pip                ...
requests           ...
urllib3            ...

Tu remarques que certifi, idna, urllib3… ont été installés automatiquement : ce sont les dépendances de requests.

3.3 Désinstaller

pip uninstall requests        # demande confirmation (y/n)
pip uninstall -y requests     # sans confirmation

⚠️ pip uninstall ne retire pas les dépendances qui avaient été installées avec le paquet. Si tu veux repartir de zéro, le plus simple reste de supprimer et recréer le venv.

3.4 pip freeze et le fichier requirements.txt

Comment dire à un ami (ou à ton « toi » du futur) quelles bibliothèques installer pour faire tourner ton projet ? On écrit une liste de courses : le fichier requirements.txt.

pip freeze

affiche tous les paquets installés au format nom==version :

certifi==2026.7.22
charset-normalizer==3.5.2
idna==3.20
requests==2.34.2
urllib3==2.8.0

On redirige cette sortie dans un fichier avec > :

pip freeze > requirements.txt

Et de l'autre côté, sur un autre ordinateur, après avoir créé et activé un venv :

pip install -r requirements.txt

-r signifie « lis la liste dans ce fichier ». En une commande, on retrouve exactement les mêmes versions. 🎉

Tu peux aussi écrire requirements.txt à la main, avec seulement les paquets que tu utilises directement :

# requirements.txt — dépendances de mon projet
requests>=2.30
rich
pandas==2.2.2
Notation Signification
rich n'importe quelle version (la plus récente)
pandas==2.2.2 exactement cette version (épinglée)
requests>=2.30 au moins 2.30
flask~=3.0 compatible avec 3.0 (donc 3.x, mais pas 4.0)

💡 freeze ou à la main ? pip freeze donne une reproduction exacte (idéal pour déployer une application). Une liste écrite à la main est plus lisible. Pour débuter, pip freeze > requirements.txt est parfaitement acceptable.

3.5 Le cycle de vie complet

# 1. Je crée mon projet et son venv
mkdir meteo && cd meteo
python3 -m venv .venv            # Windows : py -m venv .venv
source .venv/bin/activate        # Windows : .venv\Scripts\Activate.ps1

# 2. J'installe ce dont j'ai besoin
pip install requests rich

# 3. Je code… puis je sauvegarde la liste
pip freeze > requirements.txt

# 4. Je termine ma session
deactivate

Et pour quelqu'un qui récupère le projet :

cd meteo
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python main.py

4. L'alternative moderne : pyproject.toml et uv

Tu croiseras ces deux noms partout ; voici l'essentiel pour ne pas être perdu.

4.1 pyproject.toml

pyproject.toml est le fichier de configuration standard d'un projet Python moderne. Il remplace (et dépasse) requirements.txt : il décrit le nom du projet, sa version, la version de Python requise, ses dépendances, et la configuration des outils (pytest, ruff… que tu verras au chapitre 13).

[project]
name = "meteo"
version = "0.1.0"
description = "Affiche la météo dans le terminal"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "requests>=2.30",
    "rich",
]

[project.optional-dependencies]
dev = ["pytest", "ruff"]

Le format TOML se lit facilement : des sections entre crochets [...] et des paires clé = valeur.

4.2 uv

uv est un outil récent, extrêmement rapide, qui remplace en une seule commande venv + pip (et bien plus : il peut même installer Python pour toi). Il s'installe à part (voir docs.astral.sh/uv). Un aperçu :

uv init meteo          # crée le projet avec un pyproject.toml
cd meteo
uv add requests rich   # ajoute les dépendances (et crée le .venv tout seul)
uv run main.py         # exécute le script dans le venv, sans l'activer

Il sait aussi imiter les commandes classiques :

uv venv                              # crée .venv
uv pip install -r requirements.txt   # comme pip, en beaucoup plus rapide

🧭 Que choisir ? Apprends d'abord venv + pip + requirements.txt : ils sont livrés avec Python, fonctionnent partout et t'aident à comprendre ce qui se passe. Quand tu seras à l'aise, essaie uv : tu retrouveras les mêmes concepts, en plus confortable.

5. Git : la machine à remonter le temps

Tu as sûrement déjà vu des dossiers comme ça :

rapport.docx
rapport_v2.docx
rapport_v2_corrige.docx
rapport_FINAL.docx
rapport_FINAL_vraiment.docx

Git résout ce problème proprement. C'est un logiciel de gestion de versions : il enregistre des photos successives de ton projet (appelées commits), avec un message expliquant ce qui a changé. Tu peux à tout moment :

  • voir l'historique complet ;
  • comparer deux versions ;
  • revenir en arrière ;
  • essayer une idée dans une « réalité parallèle » (une branche) sans risque ;
  • collaborer à plusieurs sur le même code.

💡 Git ≠ GitHub. Git est le logiciel qui tourne sur ton ordinateur. GitHub est un site web qui héberge des dépôts Git en ligne (comme GitLab, Codeberg ou Bitbucket). Tu peux utiliser Git sans GitHub, mais pas l'inverse.

5.1 Installer et configurer Git

  • Windows : télécharge « Git for Windows » sur git-scm.com et garde les options par défaut (il installe aussi Git Bash).
  • macOS : tape git --version dans le terminal ; macOS propose d'installer les outils nécessaires. Ou brew install git.
  • Linux : sudo apt install git (Debian/Ubuntu) ou sudo dnf install git (Fedora).

Vérifie : git --version.

Ensuite, présente-toi à Git une seule fois (ces infos apparaissent dans chaque commit) :

git config --global user.name "Camille Martin"
git config --global user.email "camille@example.com"
git config --global init.defaultBranch main

La troisième ligne nomme main la branche principale des nouveaux dépôts (c'est le nom utilisé par GitHub). Pour voir ta configuration : git config --list.

💡 Si tu comptes publier sur GitHub, utilise la même adresse e-mail que ton compte GitHub (ou l'adresse anonyme ...@users.noreply.github.com que GitHub te fournit dans ses réglages).

5.2 Les trois zones de Git

C'est LE concept à comprendre. Pense à un photographe qui prépare une photo de groupe :

Zone Analogie Commande pour y envoyer
Répertoire de travail (working directory) les gens qui se baladent dans la pièce tu modifies tes fichiers
Zone de préparation (staging area / index) les gens que tu places devant l'objectif git add
Dépôt (repository, dossier .git) l'album photo git commit
 fichiers modifiés  ──git add──▶  zone de préparation  ──git commit──▶  historique

Pourquoi une étape intermédiaire ? Pour choisir ce qui va dans chaque photo. Si tu as corrigé un bug ET commencé une nouvelle fonction, tu peux faire deux commits séparés, chacun avec un message clair.

5.3 Créer un dépôt : git init

cd mon_projet
git init

Git crée un dossier caché .git : c'est là qu'il stocke tout l'historique. Ne le modifie jamais à la main. Supprimer .git = supprimer tout l'historique (tes fichiers restent, mais ne sont plus suivis).

5.4 Observer : git status

git status est ta boussole. Lance-la tout le temps, avant et après chaque commande.

git status
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
    .gitignore
    main.py

nothing added to commit but untracked files present (use "git add" to track)

Git te dit : « je vois deux fichiers, mais je ne les suis pas encore (untracked) ». Et il te dit même quoi faire !

5.5 Préparer : git add

git add main.py          # un fichier précis
git add src/             # tout un dossier
git add .                # tout ce qui a changé dans le dossier courant

Pour retirer un fichier de la zone de préparation (sans perdre tes modifications) :

git restore --staged main.py

5.6 Enregistrer : git commit

git commit -m "Premier commit : script de base"
[main (root-commit) b83e225] Premier commit : script de base
 2 files changed, 5 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 main.py

b83e225 est l'identifiant (le hash) du commit, une empreinte unique. Chaque commit connaît son « parent » : l'historique forme une chaîne.

Écrire un bon message de commit :

  • court (moins de 70 caractères environ) ;
  • à l'impératif ou à l'indicatif, qui décrit ce que fait le commit : « Ajoute le calcul de la moyenne », « Corrige la division par zéro » ;
  • ❌ à éviter : « modif », « wip », « ça marche enfin », « azerty ».

Raccourci pour les fichiers déjà suivis : git commit -am "message" fait add + commit d'un coup (mais n'ajoute pas les nouveaux fichiers).

🎯 Quand committer ? Dès que tu as une petite étape qui fonctionne : une fonction terminée, un bug corrigé. Mieux vaut beaucoup de petits commits qu'un énorme commit « tout le projet ».

5.7 Consulter l'historique : git log

git log                     # historique détaillé (q pour quitter)
git log --oneline           # une ligne par commit
git log --oneline --graph   # avec un dessin des branches
4f5b154 Personnalise la salutation
b83e225 Premier commit : script de base

💡 git log s'ouvre dans un afficheur : flèches pour défiler, q pour quitter. Beaucoup de débutants restent coincés ici !

5.8 Voir les différences : git diff

git diff              # modifications pas encore préparées (non « add »)
git diff --staged     # modifications préparées, prêtes à être commitées
git diff b83e225      # différences entre ce commit et maintenant
diff --git a/main.py b/main.py
--- a/main.py
+++ b/main.py
@@ -1 +1,2 @@
-print("Bonjour")
+nom = input("Nom ? ")
+print(f"Bonjour {nom}")

Les lignes en - (rouge) ont été supprimées, celles en + (vert) ajoutées.

5.9 Annuler des choses

Je veux… Commande
annuler mes modifications d'un fichier (revenir au dernier commit) git restore main.py ⚠️ perte définitive des modifs
retirer un fichier de la zone de préparation git restore --staged main.py
voir un ancien fichier sans rien casser git show b83e225:main.py
corriger le message du dernier commit (pas encore publié) git commit --amend -m "Nouveau message"
annuler un commit en créant un commit inverse (sûr, même publié) git revert 4f5b154

⚠️ Tu verras parfois git reset --hard sur Internet. Cette commande efface du travail sans retour possible. Évite-la tant que tu ne la maîtrises pas.

6. Le fichier .gitignore

Certains fichiers ne doivent jamais entrer dans Git :

  • le dossier .venv/ (énorme, spécifique à ta machine, se recrée avec requirements.txt) ;
  • les caches __pycache__/ et fichiers *.pyc (générés automatiquement par Python) ;
  • les secrets : mots de passe, clés d'API, fichier .env ;
  • les fichiers propres à ton système ou ton éditeur (.DS_Store, Thumbs.db, .vscode/…).

On les liste dans un fichier texte nommé exactement .gitignore, à la racine du projet. Voici un bon point de départ pour Python :

# Environnements virtuels
.venv/
venv/
env/

# Fichiers compilés de Python
__pycache__/
*.py[cod]

# Tests et outils
.pytest_cache/
.ruff_cache/
.coverage
htmlcov/

# Distribution / packaging
build/
dist/
*.egg-info/

# Secrets et réglages locaux
.env

# Éditeurs et systèmes d'exploitation
.vscode/
.idea/
.DS_Store
Thumbs.db

Syntaxe :

  • dossier/ ignore un dossier entier ;
  • *.log ignore tous les fichiers finissant par .log ;
  • # ... est un commentaire ;
  • !important.log crée une exception (à ne pas ignorer).

💡 Crée le .gitignore AVANT ton premier commit. GitHub propose aussi un modèle « Python » tout prêt quand tu crées un dépôt.

Pour vérifier pourquoi un fichier est ignoré :

git check-ignore -v .venv/pyvenv.cfg

Si tu as déjà commité un fichier par erreur, l'ajouter au .gitignore ne suffit pas : Git continue de le suivre. Il faut le retirer du suivi (sans le supprimer de ton disque) :

git rm -r --cached .venv
echo ".venv/" >> .gitignore
git add .gitignore
git commit -m "Retire .venv du suivi Git"

🔐 Secret commité = secret compromis. Si tu as publié une clé d'API ou un mot de passe sur GitHub, le retirer dans un nouveau commit ne suffit pas : il reste dans l'historique. Change immédiatement ce mot de passe / révoque cette clé.

7. Les branches

Une branche est une ligne de développement parallèle. Analogie : tu écris un roman, et tu veux tester une fin alternative. Plutôt que de raturer ton manuscrit, tu fais une photocopie et tu écris la nouvelle fin dessus. Si elle te plaît, tu l'intègres au manuscrit ; sinon, tu jettes la photocopie.

main     : A ── B ──────────── E  (fusion)
                 \            /
nouvelle-fin :    C ──── D ──

7.1 Créer et changer de branche

git branch                       # liste les branches (* = branche actuelle)
git switch -c ajout-salutation   # crée la branche ET s'y place
git switch main                  # revient sur main
git branch -d ajout-salutation   # supprime une branche déjà fusionnée

💡 Dans de vieux tutoriels, tu verras git checkout -b nom à la place de git switch -c nom : c'est équivalent. switch est plus récent et plus clair.

Quand tu changes de branche, les fichiers de ton dossier changent réellement pour refléter l'état de cette branche. C'est normal ! Pense à committer (ou annuler) tes modifications avant de changer de branche.

7.2 Fusionner : git merge

On se place sur la branche qui reçoit les modifications, puis on fusionne :

git switch main
git merge ajout-salutation

Si main n'a pas bougé depuis la création de la branche, Git avance simplement le pointeur (fast-forward) :

Updating b83e225..4f5b154
Fast-forward
 main.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

Si les deux branches ont avancé, Git crée un commit de fusion qui réunit les deux histoires.

7.3 Résoudre un conflit

Un conflit survient quand les deux branches ont modifié la même ligne différemment. Git ne peut pas deviner laquelle garder, il te demande d'arbitrer :

Auto-merging main.py
CONFLICT (content): Merge conflict in main.py
Automatic merge failed; fix conflicts and then commit the result.

Le fichier contient alors des marqueurs :

nom = input("Nom ? ")
<<<<<<< HEAD
print(f"Bienvenue {nom}")
=======
print(f"Salut {nom} !")
>>>>>>> couleur
  • entre <<<<<<< HEAD et ======= : la version de ta branche actuelle (main) ;
  • entre ======= et >>>>>>> couleur : la version de la branche fusionnée.

Pour résoudre :

  1. Ouvre le fichier, choisis la bonne version (ou combine-les), supprime les trois lignes de marqueurs. VS Code affiche des boutons « Accept Current / Accept Incoming / Accept Both » très pratiques.
  2. Résultat, par exemple :
nom = input("Nom ? ")
print(f"Bienvenue {nom} !")
  1. Termine la fusion :
git add main.py
git commit -m "Fusionne la branche couleur"

Pas de panique : un conflit est une situation normale et sans danger. Si tu veux tout annuler et revenir avant la fusion : git merge --abort.

8. GitHub : publier et collaborer

8.1 Créer un compte et un dépôt

  1. Crée un compte gratuit sur github.com.
  2. Clique sur + (en haut à droite) → New repository.
  3. Donne un nom (ex. gestion-taches), une courte description, choisis Public ou Private.
  4. Si ton projet existe déjà en local, ne coche rien (pas de README, pas de .gitignore, pas de licence) : le dépôt doit être vide pour éviter des conflits au premier envoi.
  5. Clique sur Create repository. GitHub affiche l'adresse du dépôt, par exemple https://github.com/camille-martin/gestion-taches.git.

8.2 S'authentifier

GitHub n'accepte plus ton mot de passe dans le terminal. Trois solutions, de la plus simple à la plus technique :

  • GitHub CLI : installe gh (cli.github.com) puis lance gh auth login et laisse-toi guider. ✅ Recommandé pour débuter.
  • Git Credential Manager : inclus dans Git for Windows ; une fenêtre de connexion s'ouvre au premier push.
  • Clé SSH : plus technique, très pratique ensuite (voir la documentation GitHub « Connecting to GitHub with SSH »).

8.3 Relier ton dépôt local : git remote

Un remote est un dépôt distant, avec un surnom. Par convention, le principal s'appelle origin.

git remote add origin https://github.com/camille-martin/gestion-taches.git
git remote -v        # vérifie

8.4 Envoyer : git push

git push -u origin main

-u (ou --set-upstream) mémorise le lien entre ta branche main locale et celle de GitHub. La première fois seulement. Ensuite, un simple git push suffit.

Rafraîchis la page GitHub : ton code est en ligne ! 🚀

8.5 Récupérer : git pull et git clone

  • git clone : télécharger un dépôt pour la première fois (sur un nouvel ordinateur, ou le projet de quelqu'un d'autre).
git clone https://github.com/camille-martin/gestion-taches.git
cd gestion-taches
python3 -m venv .venv              # Windows : py -m venv .venv
source .venv/bin/activate          # Windows : .venv\Scripts\Activate.ps1
pip install -r requirements.txt

Remarque : le .venv n'est pas dans le dépôt (grâce au .gitignore), donc on le recrée. C'est exactement pour ça que requirements.txt existe.

  • git pull : récupérer les nouveaux commits d'un dépôt que tu as déjà (par exemple ceux que tu as poussés depuis un autre ordinateur, ou ceux d'un collègue).
git pull

Routine quotidienne quand tu travailles sur plusieurs machines :

git pull  →  coder  →  git add  →  git commit  →  git push

8.6 Le README.md

Le fichier README.md à la racine est la vitrine de ton projet : GitHub l'affiche automatiquement sous la liste des fichiers. Il est écrit en Markdown (le même format que ce cours). Un bon README répond à : c'est quoi ? comment l'installer ? comment l'utiliser ?

# Gestion de tâches

Petit gestionnaire de tâches en ligne de commande, écrit en Python.

## Fonctionnalités

- Ajouter, lister et cocher des tâches
- Sauvegarde automatique dans un fichier JSON

## Installation

```bash
git clone https://github.com/camille-martin/gestion-taches.git
cd gestion-taches
python3 -m venv .venv
source .venv/bin/activate      # Windows : .venv\Scripts\Activate.ps1
pip install -r requirements.txt
```

## Utilisation

```bash
python main.py
```

## Auteur

Camille Martin — projet réalisé en apprenant Python.

8.7 Pour aller plus loin sur GitHub

  • Issues : liste de bugs et d'idées pour ton projet.
  • Pull requests : proposer des modifications (depuis une branche) et les faire relire avant de fusionner. C'est la base du travail en équipe et de la contribution aux projets open source.
  • Fork : copier le dépôt de quelqu'un d'autre dans ton compte pour y contribuer.
  • Licence : un fichier LICENSE (MIT, GPL…) indique ce que les autres ont le droit de faire avec ton code.

9. Résumé des commandes

Besoin Commande
Créer un venv python3 -m venv .venv (Windows : py -m venv .venv)
Activer (macOS/Linux) source .venv/bin/activate
Activer (Windows PowerShell) .venv\Scripts\Activate.ps1
Désactiver deactivate
Installer / désinstaller pip install x / pip uninstall x
Lister pip list / pip freeze
Sauver / restaurer les dépendances pip freeze > requirements.txt / pip install -r requirements.txt
Nouveau dépôt Git git init
État git status
Préparer / enregistrer git add . / git commit -m "..."
Historique / différences git log --oneline / git diff
Branches git switch -c x / git switch main / git merge x
GitHub git remote add origin URL / git push -u origin main / git pull / git clone URL

Pièges fréquents

1. Oublier d'activer le venv

Symptôme : tu as fait pip install requests, mais import requests donne ModuleNotFoundError: No module named 'requests'. Ou l'inverse : le paquet s'installe dans le Python global.

Correction : vérifie que (.venv) apparaît au début de l'invite. Sinon, active le venv (source .venv/bin/activate ou .venv\Scripts\Activate.ps1). Dans VS Code, vérifie aussi l'interpréteur sélectionné (en bas à droite de la fenêtre) : il doit pointer vers .venv.

2. Committer le dossier .venv

Symptôme : git status affiche des milliers de fichiers, le push est interminable, et le dépôt GitHub contient un dossier .venv énorme qui ne fonctionne de toute façon pas sur un autre ordinateur.

Correction : ajoute .venv/ au .gitignore, puis retire-le du suivi : git rm -r --cached .venv, et committe. À l'avenir, crée le .gitignore avant le premier git add.

3. Pas de .gitignore (ou mal nommé)

Symptôme : __pycache__/ et .pyc apparaissent dans chaque commit ; ou ton fichier s'appelle gitignore.txt / .gitignore.txt et n'a aucun effet.

Correction : le fichier doit s'appeler exactement .gitignore (avec le point, sans extension), et être à la racine du projet. Sous Windows, active « Afficher les extensions de fichiers » dans l'explorateur pour vérifier qu'il n'y a pas de .txt caché.

4. Lancer git init dans le mauvais dossier

Symptôme : git status liste tout ton dossier personnel (Documents, Images…), ou tu as un dépôt dans un dépôt.

Correction : vérifie où tu es avec pwd (macOS/Linux) ou Get-Location (PowerShell) avant git init. Pour annuler un git init malencontreux, supprime le dossier .git créé au mauvais endroit (et seulement celui-là !).

5. requirements.txt oublié ou pas à jour

Symptôme : le projet fonctionne chez toi, mais chez ton ami (ou après un git clone), on obtient ModuleNotFoundError.

Correction : après chaque pip install d'un nouveau paquet, régénère pip freeze > requirements.txt et committe ce fichier. Teste de temps en temps : supprime .venv, recrée-le, pip install -r requirements.txt, et lance ton programme.

6. Rester coincé dans git log ou dans l'éditeur de commit

Symptôme : après git log, le terminal n'accepte plus de commande ; ou après git commit (sans -m), un écran étrange (Vim) s'ouvre.

Correction : dans git log, appuie sur q. Dans Vim : tape ton message après avoir appuyé sur i, puis Échap, puis :wq et Entrée (ou :q! pour abandonner). Pour éviter ça, utilise toujours git commit -m "message", ou configure VS Code comme éditeur : git config --global core.editor "code --wait".

7. Le premier push est refusé

Symptôme : ! [rejected] main -> main (fetch first) ou failed to push some refs.

Correction : le dépôt GitHub contient des commits que tu n'as pas (souvent parce que tu as coché « Add a README » à la création). Récupère-les d'abord : git pull origin main --allow-unrelated-histories, résous un éventuel conflit, puis git push. Pour l'éviter : crée le dépôt GitHub vide quand ton projet existe déjà en local.

8. Laisser des marqueurs de conflit dans le code

Symptôme : SyntaxError sur une ligne <<<<<<< HEAD après une fusion.

Correction : un conflit n'est résolu que lorsque toutes les lignes <<<<<<<, ======= et >>>>>>> ont disparu. Cherche-les (Ctrl+F sur <<<<<<<), corrige, puis git add et git commit.


Exercices

Exercice 1 ⭐ — Premier venv

  1. Crée un dossier test_venv et ouvre un terminal dedans.
  2. Crée un environnement virtuel .venv puis active-le.
  3. Vérifie que tu es dans le venv avec une commande Python d'une ligne.
  4. Désactive-le, relance la même commande : que constates-tu ?

Écris la suite exacte de commandes pour ton système (et, si tu peux, pour l'autre).

Exercice 2 ⭐ — Suis-je dans un venv ?

Écris un script verifier_venv.py qui affiche « ✅ Tu es dans un environnement virtuel » suivi du dossier du venv, ou « ⚠️ Tu utilises le Python global du système » suivi du chemin de l'interpréteur.

Indice : dans un venv, sys.prefix (le dossier du Python en cours) est différent de sys.base_prefix (le dossier du Python d'origine). sys.executable donne le chemin de l'interpréteur.

Lance-le avec et sans venv activé.

Exercice 3 ⭐ — La liste de courses

Dans ton venv de l'exercice 1 :

  1. installe le paquet rich ;
  2. affiche la liste des paquets installés ;
  3. génère un requirements.txt ;
  4. supprime le venv, recrée-le, et réinstalle tout à partir du requirements.txt ;
  5. vérifie que ça marche avec python -c "from rich import print; print('[bold green]Ça marche ![/]')".

Exercice 4 ⭐⭐ — Trier le bon grain de l'ivraie

Voici le contenu d'un dossier de projet :

calculatrice/
├── .venv/
├── __pycache__/operations.cpython-312.pyc
├── .env                  (contient API_KEY=abc123)
├── .vscode/settings.json
├── operations.py
├── main.py
├── requirements.txt
├── README.md
├── notes_perso.txt
└── historique.log
  1. Indique, pour chaque élément, s'il doit être commité ou ignoré, et pourquoi.
  2. Écris le .gitignore correspondant (on décide que les fichiers .log et notes_perso.txt sont personnels).

Exercice 5 ⭐⭐ — Trois commits et un retour en arrière

  1. Crée un dossier journal, initialise un dépôt Git et un .gitignore Python.
  2. Crée journal.py qui affiche « Mon journal ». Committe.
  3. Ajoute une ligne qui affiche la date du jour (avec datetime). Avant de committer, affiche les différences. Committe.
  4. Ajoute une ligne qui affiche « Bonne journée ! ». Committe.
  5. Affiche l'historique sur une ligne par commit.
  6. Modifie journal.py en y écrivant n'importe quoi, puis annule cette modification avec Git.
  7. Affiche le contenu de journal.py tel qu'il était au tout premier commit, sans rien modifier.

Exercice 6 ⭐⭐ — Branches et conflit volontaire

Dans le dépôt de l'exercice 5 :

  1. Crée une branche anglais et change « Bonne journée ! » en « Have a nice day! ». Committe.
  2. Reviens sur main et change « Bonne journée ! » en « Belle journée à toi ! ». Committe.
  3. Fusionne anglais dans main : un conflit apparaît.
  4. Résous-le en gardant les deux phrases, sur deux lignes. Termine la fusion.
  5. Affiche le graphe de l'historique, puis supprime la branche anglais.

Exercice 7 ⭐⭐⭐ — Analyseur de requirements.txt

Écris un script analyser_requirements.py qui lit un fichier requirements.txt et affiche :

  • la liste des paquets épinglés (nom==version) avec leur version ;
  • la liste des paquets non épinglés (sans version, ou avec >=, ~=…), accompagnés d'un avertissement.

Le script doit ignorer les lignes vides et les commentaires (ce qui suit un #, y compris en fin de ligne). Écris une fonction analyser_ligne(ligne) qui renvoie un tuple (nom, version) (avec version = None si non épinglé) ou None pour une ligne à ignorer.

Teste avec :

# Dépendances du projet
requests==2.32.3
rich>=13.0

flask  # serveur web
pandas==2.2.2

Exercice 8 ⭐⭐⭐ — Générateur de projet

Écris un script nouveau_projet.py qui, lancé avec python nouveau_projet.py mon_projet :

  1. crée le dossier mon_projet (et s'arrête avec un message d'erreur s'il existe déjà) ;
  2. y écrit un .gitignore Python, un README.md (titre = nom du projet), un requirements.txt vide et un main.py minimal avec if __name__ == "__main__": ;
  3. crée un venv .venv dans ce dossier ;
  4. initialise un dépôt Git et fait un premier commit « Initialisation du projet ».

Indices : pathlib.Path pour les fichiers (chapitre 07), sys.argv pour les arguments, et subprocess.run([...], cwd=dossier, check=True) pour lancer une commande dans un dossier donné. sys.executable est le chemin du Python en cours.


Mini-projet

🚀 Publier un de tes projets sur GitHub

Tu as écrit plusieurs programmes depuis le début du cours : un jeu de devinette, un gestionnaire de contacts, un carnet de notes en JSON (chapitre 07), des classes (chapitre 10)… Il est temps d'en choisir un et de lui donner une vraie vie de projet : versionné, documenté, publié.

Cahier des charges

Ton projet publié doit :

  1. avoir son propre dossier avec un environnement virtuel .venv ;
  2. contenir un requirements.txt à jour (même s'il est vide parce que tu n'utilises que la bibliothèque standard — dans ce cas, écris-y un commentaire # Aucune dépendance externe) ;
  3. contenir un .gitignore Python complet ;
  4. contenir un README.md avec : titre, description, fonctionnalités, installation, utilisation, auteur ;
  5. avoir au moins 4 commits avec des messages clairs (pas un seul commit géant) ;
  6. avoir utilisé au moins une branche fusionnée dans main pour ajouter une fonctionnalité ou une amélioration ;
  7. être publié sur GitHub ;
  8. être clonable et exécutable : un git clone dans un autre dossier, suivi de l'installation décrite dans le README, doit permettre de lancer le programme.

Étapes suggérées

  1. Choisir et ranger : copie ton projet dans un nouveau dossier (ex. ~/projets/carnet-notes). Supprime les fichiers inutiles.
  2. Environnement : crée et active le venv, installe les éventuelles dépendances, génère requirements.txt.
  3. Git : crée le .gitignore, git init, vérifie avec git status que .venv n'apparaît pas, premier commit.
  4. README : rédige-le, committe-le séparément.
  5. Branche : crée une branche amelioration, ajoute une petite fonctionnalité (un message d'accueil, une option de menu, une validation de saisie…), committe, fusionne dans main.
  6. GitHub : crée le dépôt vide, ajoute le remote, pousse.
  7. Test de vérité : dans un autre dossier, git clone, recrée le venv, installe, lance. Si quelque chose manque, corrige, committe et pousse à nouveau.

Pistes d'amélioration

  • Ajoute un fichier LICENSE (GitHub propose « Add file → Create new file » puis un modèle de licence MIT).
  • Ajoute une capture d'écran ou un exemple de sortie du terminal dans le README.
  • Crée deux ou trois issues sur GitHub pour noter tes idées d'améliorations, puis ferme-les avec un commit contenant Closes #1 dans le message.
  • Travaille depuis un deuxième ordinateur (ou un deuxième dossier) et entraîne-toi au cycle pull → modifier → push.
  • Remplace requirements.txt par un pyproject.toml et essaie uv.

Corrigés

Corrigé de l'exercice 1 **macOS / Linux :**
mkdir test_venv
cd test_venv
python3 -m venv .venv
source .venv/bin/activate
python -c "import sys; print(sys.prefix)"
# → /home/camille/test_venv/.venv   (se termine par .venv)
deactivate
python3 -c "import sys; print(sys.prefix)"
# → /usr   (le Python du système)
**Windows PowerShell :**
mkdir test_venv
cd test_venv
py -m venv .venv
.venv\Scripts\Activate.ps1
python -c "import sys; print(sys.prefix)"
# → C:\Users\Camille\test_venv\.venv
deactivate
py -c "import sys; print(sys.prefix)"
# → C:\Users\Camille\AppData\Local\Programs\Python\Python313
**Constat** : venv activé, `sys.prefix` pointe vers le dossier `.venv` ; une fois désactivé, il pointe vers l'installation globale de Python. C'est la preuve que l'activation change le Python utilisé. Note aussi que dans le venv, `python` suffit, alors qu'en dehors on écrit `python3` (macOS/Linux) ou `py` (Windows).
Corrigé de l'exercice 2
import sys


def est_dans_un_venv() -> bool:
    """Renvoie True si le script tourne dans un environnement virtuel."""
    return sys.prefix != sys.base_prefix


if est_dans_un_venv():
    print("✅ Tu es dans un environnement virtuel.")
    print(f"   Dossier du venv : {sys.prefix}")
else:
    print("⚠️  Tu utilises le Python global du système.")
    print(f"   Python utilisé : {sys.executable}")
Exécution sans venv puis avec :
$ python3 verifier_venv.py
⚠️  Tu utilises le Python global du système.
   Python utilisé : /usr/bin/python3

$ source .venv/bin/activate
(.venv) $ python verifier_venv.py
✅ Tu es dans un environnement virtuel.
   Dossier du venv : /home/camille/test_venv/.venv
**Explication** : quand Python démarre depuis un venv, il garde en mémoire deux informations : `sys.base_prefix` (l'installation d'origine qui a servi à créer le venv) et `sys.prefix` (le venv lui-même). Hors venv, les deux sont identiques. Comparer les deux est donc la méthode officielle pour détecter un venv.
Corrigé de l'exercice 3
# (venv activé)
pip install rich
pip list
pip freeze > requirements.txt
cat requirements.txt          # Windows PowerShell : Get-Content requirements.txt

deactivate
rm -rf .venv                  # Windows : Remove-Item -Recurse -Force .venv
python3 -m venv .venv         # Windows : py -m venv .venv
source .venv/bin/activate     # Windows : .venv\Scripts\Activate.ps1
pip install -r requirements.txt
python -c "from rich import print; print('[bold green]Ça marche ![/]')"
`requirements.txt` contient `rich==...` **et** ses dépendances (`markdown-it-py`, `mdurl`, `pygments`…), car `pip freeze` liste *tout* ce qui est installé. Après la recréation, `pip install -r` réinstalle exactement les mêmes versions : le venv est bien jetable et reproductible.
Corrigé de l'exercice 4 | Élément | Décision | Raison | |---|---|---| | `.venv/` | ❌ ignorer | lourd, spécifique à la machine, se recrée avec `requirements.txt` | | `__pycache__/` | ❌ ignorer | généré automatiquement par Python | | `.env` | ❌ ignorer | **contient un secret** (clé d'API) | | `.vscode/` | ❌ ignorer | réglages personnels de l'éditeur | | `operations.py`, `main.py` | ✅ commiter | c'est le code du projet | | `requirements.txt` | ✅ commiter | indispensable pour réinstaller les dépendances | | `README.md` | ✅ commiter | documentation du projet | | `notes_perso.txt` | ❌ ignorer | personnel (décision de l'énoncé) | | `historique.log` | ❌ ignorer | fichier généré à l'exécution |
# Environnement virtuel
.venv/

# Fichiers compilés
__pycache__/
*.pyc

# Secrets
.env

# Éditeur
.vscode/

# Fichiers personnels et générés
notes_perso.txt
*.log
Pour vérifier : `git status` ne doit plus montrer que `operations.py`, `main.py`, `requirements.txt`, `README.md` et `.gitignore`. **Bonne pratique** : fournis un fichier `.env.example` (commité) qui contient `API_KEY=ta_cle_ici`, pour indiquer aux autres quelles variables définir sans divulguer les tiennes.
Corrigé de l'exercice 5
mkdir journal && cd journal
git init
printf '.venv/\n__pycache__/\n*.pyc\n.env\n' > .gitignore   # ou crée-le dans VS Code
`journal.py`, version 1 :
print("Mon journal")
git add .
git commit -m "Crée le script du journal"
Version 2 :
from datetime import date

print("Mon journal")
print(f"Nous sommes le {date.today():%d/%m/%Y}")
git diff                      # montre les lignes ajoutées en +
git commit -am "Affiche la date du jour"
Version 3 : ajoute `print("Bonne journée !")`, puis :
git commit -am "Ajoute un message de fin"
git log --oneline
c3d4e5f Ajoute un message de fin
b2c3d4e Affiche la date du jour
a1b2c3d Crée le script du journal
Étape 6, annuler une modification non commitée :
git status                    # journal.py apparaît en "modified"
git restore journal.py        # retour à la version du dernier commit
git status                    # "nothing to commit, working tree clean"
Étape 7, consulter le premier commit **sans rien modifier** (remplace `a1b2c3d` par ton identifiant réel, lu dans `git log --oneline`) :
git show a1b2c3d:journal.py
`git show identifiant:chemin` affiche le contenu d'un fichier à un instant donné de l'historique, sans toucher à ton dossier de travail.
Corrigé de l'exercice 6
git switch -c anglais
# dans journal.py : "Bonne journée !" → "Have a nice day!"
git commit -am "Traduit le message de fin en anglais"

git switch main
# dans journal.py : "Bonne journée !" → "Belle journée à toi !"
git commit -am "Reformule le message de fin"

git merge anglais
Auto-merging journal.py
CONFLICT (content): Merge conflict in journal.py
Automatic merge failed; fix conflicts and then commit the result.
Le fichier contient :
<<<<<<< HEAD
print("Belle journée à toi !")
=======
print("Have a nice day!")
>>>>>>> anglais
On garde les deux et on **supprime les marqueurs** :
print("Belle journée à toi !")
print("Have a nice day!")
python journal.py                 # on vérifie que le script fonctionne
git add journal.py
git commit -m "Fusionne la branche anglais"
git log --oneline --graph
git branch -d anglais
*   e5f6a7b Fusionne la branche anglais
|\
| * d4e5f6a Traduit le message de fin en anglais
* | c4d5e6f Reformule le message de fin
|/
* c3d4e5f Ajoute un message de fin
...
Le graphe montre bien les deux lignes parallèles qui se rejoignent dans le commit de fusion. `git branch -d` accepte de supprimer `anglais` car elle est entièrement fusionnée.
Corrigé de l'exercice 7
from pathlib import Path


def analyser_ligne(ligne: str) -> tuple[str, str | None] | None:
    """Analyse une ligne de requirements.txt.

    Renvoie (nom, version) ou None si la ligne est vide ou un commentaire.
    version vaut None si le paquet n'est pas épinglé avec ==.
    """
    ligne = ligne.split("#", 1)[0].strip()   # retire les commentaires
    if not ligne:
        return None
    if "==" in ligne:
        nom, version = ligne.split("==", 1)
        return nom.strip(), version.strip()
    # >=, <=, ~= ou rien du tout : on récupère juste le nom
    for separateur in (">=", "<=", "~=", ">", "<"):
        if separateur in ligne:
            return ligne.split(separateur, 1)[0].strip(), None
    return ligne, None


def analyser_fichier(chemin: Path) -> None:
    epingles = []
    non_epingles = []
    for ligne in chemin.read_text(encoding="utf-8").splitlines():
        resultat = analyser_ligne(ligne)
        if resultat is None:
            continue
        nom, version = resultat
        if version is None:
            non_epingles.append(nom)
        else:
            epingles.append(f"{nom} ({version})")

    print(f"Paquets épinglés ({len(epingles)}) :")
    for paquet in epingles:
        print(f"  - {paquet}")
    print(f"Paquets NON épinglés ({len(non_epingles)}) :")
    for nom in non_epingles:
        print(f"  - {nom}  ⚠️  version libre")


if __name__ == "__main__":
    analyser_fichier(Path("requirements.txt"))
Sortie avec le fichier de l'énoncé :
Paquets épinglés (2) :
  - requests (2.32.3)
  - pandas (2.2.2)
Paquets NON épinglés (2) :
  - rich  ⚠️  version libre
  - flask  ⚠️  version libre
**Points clés** : - `split("#", 1)[0]` garde ce qui est **avant** le premier `#` : ça élimine à la fois les lignes de commentaire et les commentaires de fin de ligne. - Pour les autres opérateurs (`>=`, `~=`…), le nom du paquet est simplement tout ce qui précède l'opérateur : `"rich>=13.0".split(">=", 1)[0]` donne `"rich"`. - La fonction `analyser_ligne` est **pure** (pas d'affichage, pas de fichier) : elle sera très facile à tester avec pytest au [chapitre 13](13-tests-pytest-bonnes-pratiques.md).
Corrigé de l'exercice 8
"""Crée le squelette d'un nouveau projet Python prêt pour Git.

Usage : python nouveau_projet.py nom_du_projet
"""
import subprocess
import sys
from pathlib import Path

GITIGNORE = """\
# Environnements virtuels
.venv/
venv/

# Fichiers compilés de Python
__pycache__/
*.pyc

# Secrets et réglages locaux
.env

# Éditeurs et système
.vscode/
.idea/
.DS_Store
Thumbs.db
"""


def creer_projet(nom: str) -> Path:
    dossier = Path(nom)
    if dossier.exists():
        raise SystemExit(f"Erreur : le dossier « {nom} » existe déjà.")

    dossier.mkdir()
    (dossier / ".gitignore").write_text(GITIGNORE, encoding="utf-8")
    (dossier / "README.md").write_text(
        f"# {nom}\n\nDescription du projet.\n", encoding="utf-8"
    )
    (dossier / "requirements.txt").write_text("", encoding="utf-8")
    (dossier / "main.py").write_text(
        'def main():\n    print("Bonjour !")\n\n\n'
        'if __name__ == "__main__":\n    main()\n',
        encoding="utf-8",
    )

    # sys.executable = le Python qui exécute ce script
    subprocess.run([sys.executable, "-m", "venv", ".venv"], cwd=dossier, check=True)
    subprocess.run(["git", "init"], cwd=dossier, check=True)
    subprocess.run(["git", "add", "."], cwd=dossier, check=True)
    subprocess.run(
        ["git", "commit", "-m", "Initialisation du projet"], cwd=dossier, check=True
    )
    return dossier


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage : python nouveau_projet.py nom_du_projet")
        sys.exit(1)
    chemin = creer_projet(sys.argv[1])
    print(f"Projet créé dans {chemin.resolve()}")
Vérification :
python3 nouveau_projet.py demo_projet
cd demo_projet
git log --oneline      # → 1 commit "Initialisation du projet"
git ls-files           # → .gitignore, README.md, main.py, requirements.txt
`.venv` existe bien dans le dossier mais n'est **pas** dans `git ls-files` : le `.gitignore` a été écrit **avant** le `git add .`, l'ordre des opérations est essentiel. **Remarques** : - `subprocess.run(..., check=True)` lève une exception si la commande échoue (par exemple si Git n'est pas installé) : le programme ne continue pas en silence sur une erreur. - `cwd=dossier` exécute la commande *dans* le nouveau dossier, sans changer le dossier courant du script. - `raise SystemExit("message")` affiche le message et quitte le programme avec un code d'erreur. - Si Git n'a pas ton nom et ton e-mail, le commit échoue : pense au `git config --global` de la section 5.1.
Corrigé du mini-projet Exemple avec un carnet de notes (`carnet-notes`). Les commandes sont pour macOS/Linux ; les variantes Windows sont en commentaire. **1–2. Dossier et environnement**
mkdir -p ~/projets/carnet-notes && cd ~/projets/carnet-notes
# (copie ici tes fichiers : main.py, carnet.py…)
python3 -m venv .venv                 # Windows : py -m venv .venv
source .venv/bin/activate             # Windows : .venv\Scripts\Activate.ps1
# Si le projet n'a aucune dépendance externe :
echo "# Aucune dépendance externe" > requirements.txt
# Sinon : pip install ... puis pip freeze > requirements.txt
**3. Git et premier commit** Crée le `.gitignore` de la section 6, puis :
git init
git status                            # .venv NE doit PAS apparaître
git add .
git commit -m "Version initiale du carnet de notes"
**4. README** Écris `README.md` en suivant le modèle de la section 8.6, puis :
git add README.md
git commit -m "Ajoute le README"
**5. Branche d'amélioration**
git switch -c validation-saisie
# … code : refuser une note hors de [0, 20] avec un message clair …
python main.py                        # on teste !
git commit -am "Valide les notes saisies entre 0 et 20"
# … une deuxième petite amélioration …
git commit -am "Affiche la moyenne avec deux décimales"
git switch main
git merge validation-saisie
git branch -d validation-saisie
git log --oneline --graph             # au moins 4 commits
**6. Publication** Sur GitHub : *New repository* → nom `carnet-notes` → **ne rien cocher** → *Create*.
git remote add origin https://github.com/TON-PSEUDO/carnet-notes.git
git push -u origin main
(Si l'authentification échoue : `gh auth login`, puis relance le push.) **7. Test de vérité**
cd /tmp                                # Windows : cd $env:TEMP
git clone https://github.com/TON-PSEUDO/carnet-notes.git
cd carnet-notes
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python main.py
Si le programme démarre, ton projet est réellement **portable**. 🎉 **Grille de vérification** - [ ] `.venv` absent du dépôt GitHub - [ ] `__pycache__` absent du dépôt GitHub - [ ] README lisible sur la page d'accueil du dépôt - [ ] au moins 4 commits aux messages clairs - [ ] le graphe montre une branche fusionnée (ou ses commits) - [ ] le clone fonctionne en suivant uniquement le README **Erreurs typiques rencontrées ici** : avoir créé le dépôt GitHub avec un README (→ voir piège 7), oublier d'ajouter un fichier `.py` avant le commit (→ le clone plante avec `ModuleNotFoundError` sur ton propre module : `git status` aurait montré le fichier en *untracked*).

Récapitulatif

Points clés à retenir :

  • Un venv est un dossier .venv qui isole les bibliothèques d'un projet. Un projet = un venv.
  • Création : python3 -m venv .venv (Windows : py -m venv .venv). Activation : source .venv/bin/activate (macOS/Linux) ou .venv\Scripts\Activate.ps1 (Windows). Sortie : deactivate.
  • pip installe depuis PyPI : install, uninstall, list, show, freeze.
  • requirements.txt liste les dépendances : pip freeze > requirements.txt pour l'écrire, pip install -r requirements.txt pour tout réinstaller.
  • pyproject.toml est le format de configuration moderne ; uv est un outil rapide qui regroupe venv + pip.
  • Git enregistre des photos (commits) du projet : modifier → git add → git commit -m. git status est ta boussole.
  • git log --oneline, git diff, git restore permettent de consulter et d'annuler.
  • Le .gitignore exclut .venv/, __pycache__/, .env… et se crée avant le premier commit.
  • Les branches (git switch -c, git merge) permettent d'expérimenter sans risque ; un conflit se résout à la main, puis add + commit.
  • GitHub héberge tes dépôts : git remote add origin, git push -u origin main, git pull, git clone. Le README.md est la vitrine du projet.

Checklist :

  • Je sais créer, activer (sur mon système) et désactiver un venv.
  • Je vérifie la présence de (.venv) avant chaque pip install.
  • Je sais générer et utiliser un requirements.txt.
  • J'ai configuré mon nom et mon e-mail dans Git.
  • Je connais le cycle status → add → commit et j'écris des messages clairs.
  • J'ai un .gitignore Python dans chacun de mes projets.
  • Je sais créer une branche, la fusionner et résoudre un conflit simple.
  • J'ai publié au moins un projet sur GitHub, avec un README.
  • Je sais cloner un projet et le faire fonctionner grâce à son README.

← Chapitre précédent | Sommaire | Chapitre suivant →