# 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](11-poo-avancee-heritage-typage.md) et savoir utiliser un terminal ([Chapitre 00](00-installation-environnement.md)). > > **🎯 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. ```text 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 : ```bash # macOS / Linux python3 -m venv .venv ``` ```powershell # 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é. ```bash # macOS / Linux (bash, zsh) source .venv/bin/activate ``` ```powershell # Windows PowerShell .venv\Scripts\Activate.ps1 ``` ```bat :: 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 : ```text (.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 : > > ```powershell > 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 ```bash python -c "import sys; print(sys.prefix)" ``` Si le chemin affiché se termine par `.venv`, c'est gagné. Autre méthode : ```bash # macOS / Linux which python ``` ```powershell # Windows PowerShell Get-Command python ``` #### 2.4 Désactiver le venv ```bash 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. ```bash # macOS / Linux rm -rf .venv ``` ```powershell # 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](https://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 ```bash 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 : ```python import requests reponse = requests.get("https://api.github.com") print(reponse.status_code) # 200 si tout va bien ``` Variantes utiles : ```bash 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 ```bash 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` : ```text 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 ```bash 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`. ```bash pip freeze ``` affiche tous les paquets installés au format `nom==version` : ```text 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 `>` : ```bash pip freeze > requirements.txt ``` Et de l'autre côté, sur un autre ordinateur, après avoir créé et activé un venv : ```bash 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 : ```text # 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 ```bash # 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 : ```bash 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](13-tests-pytest-bonnes-pratiques.md)). ```toml [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](https://docs.astral.sh/uv/)). Un aperçu : ```bash 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 : ```bash 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 : ```text 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](https://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) : ```bash 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` | ```text 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` ```bash 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. ```bash git status ``` ```text On branch main No commits yet Untracked files: (use "git add ..." 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` ```bash 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) : ```bash git restore --staged main.py ``` #### 5.6 Enregistrer : `git commit` ```bash git commit -m "Premier commit : script de base" ``` ```text [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` ```bash git log # historique détaillé (q pour quitter) git log --oneline # une ligne par commit git log --oneline --graph # avec un dessin des branches ``` ```text 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` ```bash 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 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 : ```gitignore # 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é : ```bash 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) : ```bash 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. ```text main : A ── B ──────────── E (fusion) \ / nouvelle-fin : C ──── D ── ``` #### 7.1 Créer et changer de branche ```bash 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 : ```bash 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*) : ```text 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 : ```text 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 : ```text 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 : ```python nom = input("Nom ? ") print(f"Bienvenue {nom} !") ``` 3. Termine la fusion : ```bash 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](https://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](https://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`. ```bash git remote add origin https://github.com/camille-martin/gestion-taches.git git remote -v # vérifie ``` #### 8.4 Envoyer : `git push` ```bash 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). ```bash 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). ```bash git pull ``` Routine quotidienne quand tu travailles sur plusieurs machines : ```text 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 ?* ````markdown # 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 : ```text 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 : ```text # 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](07-fichiers-csv-json.md)), `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](07-fichiers-csv-json.md)), des classes ([chapitre 10](10-poo-classes-objets.md))… 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 :** ```bash 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 :** ```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 ```python 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 : ```text $ 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 ```bash # (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 | ```gitignore # 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 ```bash 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 : ```python print("Mon journal") ``` ```bash git add . git commit -m "Crée le script du journal" ``` Version 2 : ```python from datetime import date print("Mon journal") print(f"Nous sommes le {date.today():%d/%m/%Y}") ``` ```bash git diff # montre les lignes ajoutées en + git commit -am "Affiche la date du jour" ``` Version 3 : ajoute `print("Bonne journée !")`, puis : ```bash git commit -am "Ajoute un message de fin" git log --oneline ``` ```text 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 : ```bash 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`) : ```bash 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 ```bash 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 ``` ```text Auto-merging journal.py CONFLICT (content): Merge conflict in journal.py Automatic merge failed; fix conflicts and then commit the result. ``` Le fichier contient : ```text <<<<<<< HEAD print("Belle journée à toi !") ======= print("Have a nice day!") >>>>>>> anglais ``` On garde les deux et on **supprime les marqueurs** : ```python print("Belle journée à toi !") print("Have a nice day!") ``` ```bash 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 ``` ```text * 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 ```python 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é : ```text 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 ```python """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 : ```bash 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** ```bash 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 : ```bash 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 : ```bash git add README.md git commit -m "Ajoute le README" ``` **5. Branche d'amélioration** ```bash 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*. ```bash 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é** ```bash 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](11-poo-avancee-heritage-typage.md) | [Sommaire](README.md) | [Chapitre suivant →](13-tests-pytest-bonnes-pratiques.md)