# Fichier: python_cheats/cheatsheets/module_fichiers_complet.txt
# Guide Complet - Tous les Fichiers pour la Création de Modules Python
# Pour débutants - Explications détaillées de CHAQUE fichier possible


═══════════════════════════════════════════════════════════════════
INTRODUCTION - QU'EST-CE QU'UN MODULE PYTHON ?
═══════════════════════════════════════════════════════════════════

Un module Python est un package que d'autres développeurs peuvent installer
avec pip et utiliser dans leurs projets. Par exemple, quand vous faites:

    pip install requests

Vous installez le module "requests" depuis PyPI (Python Package Index).

OBJECTIF DE CE GUIDE:
Connaître TOUS les fichiers possibles pour créer un module professionnel
et comprendre le rôle de chacun.


═══════════════════════════════════════════════════════════════════
ÉTAPE 1: STRUCTURE COMPLÈTE D'UN MODULE (avec TOUS les fichiers)
═══════════════════════════════════════════════════════════════════

monmodule/                          # Dossier racine du projet
├── monmodule/                      # Package Python (même nom)
│   ├── __init__.py                 # [1] Obligatoire - Marque le package
│   ├── core.py                     # [2] Votre code principal
│   ├── utils.py                    # Fichiers additionnels
│   ├── py.typed                    # [14] Support type hints
│   └── data/                       # Données du package
│       └── config.json
├── tests/                          # Tests unitaires
│   ├── __init__.py
│   ├── conftest.py                 # [13] Fixtures pytest
│   ├── test_core.py
│   └── test_utils.py
├── docs/                           # Documentation Sphinx
│   ├── conf.py                     # [16] Config Sphinx
│   ├── index.rst                   # [17] Page d'accueil docs
│   └── api.rst
├── .github/                        # GitHub Actions
│   └── workflows/
│       └── tests.yml               # [18] CI/CD automatique
├── pyproject.toml                  # [3] Configuration moderne (OBLIGATOIRE)
├── README.md                       # [4] Documentation (OBLIGATOIRE)
├── LICENSE                         # [5] Licence (OBLIGATOIRE)
├── CHANGELOG.md                    # [6] Historique des versions
├── MANIFEST.in                     # [7] Fichiers à inclure
├── .gitignore                      # [8] Exclusions Git
├── .pre-commit-config.yaml         # [9] Hooks Git
├── requirements.txt                # [10] Dépendances de base
├── requirements-dev.txt            # [11] Dépendances développement
├── pytest.ini                      # [12] Configuration pytest
├── tox.ini                         # [13] Tests multi-versions
├── mypy.ini                        # [15] Configuration mypy
├── .editorconfig                   # [19] Style éditeurs
├── SECURITY.md                     # [20] Politique sécurité
├── CONTRIBUTING.md                 # [21] Guide contribution
├── setup.py                        # [22] LEGACY - À éviter
└── setup.cfg                       # [23] LEGACY - À éviter


═══════════════════════════════════════════════════════════════════
FICHIERS OBLIGATOIRES - NE PAS PUBLIER SANS EUX
═══════════════════════════════════════════════════════════════════

[1] __init__.py
────────────────────────────────────────────────────────────────────
   - Fichier spécial qui transforme un dossier en package Python
   - Peut être vide, mais généralement contient la version et les imports
   - Quand on fait "import monmodule", c'est ce fichier qui s'exécute
   - OBLIGATOIRE - Sans lui, Python ne reconnaît pas le dossier comme package
   
   Exemple minimal (fichier vide):
   # monmodule/__init__.py
   # (fichier vide, c'est valide)
   
   Exemple complet:
   # monmodule/__init__.py
   """
   Mon Module - Description courte
   """
   
   __version__ = "1.0.0"
   __author__ = "Votre Nom"
   
   # Importer les fonctions principales pour faciliter l'usage
   from .core import ma_fonction, MaClasse
   
   # Ce qui sera accessible avec: from monmodule import *
   __all__ = ["ma_fonction", "MaClasse"]


[2] core.py (ou tout autre nom .py)
────────────────────────────────────────────────────────────────────
   - Contient votre code réel (fonctions, classes, logique métier)
   - Vous pouvez avoir plusieurs fichiers .py (core.py, utils.py, models.py...)
   - Nom libre, mais "core.py" ou "main.py" sont des conventions courantes
   - C'est ICI que vous écrivez votre code utile
   
   Exemple:
   # monmodule/core.py
   """
   Fonctions principales du module
   """
   
   def ma_fonction(x: int, y: int) -> int:
       """Additionne deux nombres"""
       return x + y
   
   class MaClasse:
       """Une classe exemple"""
       def __init__(self, nom: str):
           self.nom = nom


[3] pyproject.toml
────────────────────────────────────────────────────────────────────
   - Fichier de configuration moderne (remplace setup.py)
   - Format TOML (plus simple que JSON, plus structuré que INI)
   - Contient: nom, version, dépendances, auteur, description, etc.
   - Standard moderne depuis PEP 517/518 (2016)
   - OBLIGATOIRE pour publier sur PyPI
   
   Sections importantes:
   - [build-system]: Comment construire le package
   - [project]: Métadonnées (nom, version, auteur...)
   - [project.dependencies]: Dépendances nécessaires
   - [project.optional-dependencies]: Dépendances optionnelles
   - [tool.*]: Configuration des outils (pytest, black, mypy...)
   
   Exemple minimal:
   [build-system]
   requires = ["setuptools>=61.0"]
   build-backend = "setuptools.build_meta"
   
   [project]
   name = "monmodule"
   version = "1.0.0"
   description = "Description courte"
   authors = [{name = "Votre Nom", email = "email@example.com"}]
   readme = "README.md"
   requires-python = ">=3.8"
   dependencies = [
       "requests>=2.28.0",
   ]


[4] README.md
────────────────────────────────────────────────────────────────────
   - Documentation principale en format Markdown
   - PREMIÈRE chose que les gens lisent sur GitHub et PyPI
   - Affiché automatiquement sur la page d'accueil GitHub
   - Devient la description longue sur PyPI si configuré dans pyproject.toml
   - OBLIGATOIRE - Un projet sans README paraît amateur
   
   Structure recommandée:
   1. Titre du projet
   2. Badges (tests, version, licence) - optionnel
   3. Description courte (1-2 phrases)
   4. Fonctionnalités principales
   5. Installation (pip install...)
   6. Exemples d'utilisation (code)
   7. Documentation complète (lien si existe)
   8. Contribution (comment aider)
   9. Licence
   
   Exemple:
   # Mon Module
   
   [![Tests](https://img.shields.io/badge/tests-passing-green)]
   
   Description courte de ce que fait le module.
   
   ## Installation
   ```bash
   pip install monmodule
   ```
   
   ## Utilisation
   ```python
   from monmodule import ma_fonction
   result = ma_fonction(2, 3)
   print(result)  # 5
   ```
   
   ## Licence
   MIT


[5] LICENSE
────────────────────────────────────────────────────────────────────
   - Fichier texte contenant le texte LÉGAL complet de votre licence
   - Détermine comment les autres peuvent utiliser/modifier/distribuer votre code
   - Sans licence = PERSONNE ne peut légalement utiliser votre code (droit d'auteur)
   - OBLIGATOIRE si vous publiez sur PyPI
   - Le texte exact doit être copié depuis la source officielle
   
   Licences courantes et leurs usages:
   
   * MIT (la plus populaire, ~60% des projets)
     - Très permissive: faire ce qu'on veut, même usage commercial
     - Seule obligation: garder le copyright
     - Recommandée pour: projets perso, open-source simple
   
   * Apache 2.0
     - Comme MIT mais avec protection explicite des brevets
     - Plus complexe mais plus sûre juridiquement
     - Recommandée pour: projets d'entreprise
   
   * GPL v3
     - Copyleft: toute modification doit aussi être GPL
     - Force le partage du code source
     - Recommandée pour: projets idéologiques pro-open-source
   
   * BSD 3-Clause
     - Très similaire à MIT
     - Clause supplémentaire sur l'usage du nom
   
   Comment choisir?
   - Projet personnel/hobby: MIT
   - Projet startup/entreprise: Apache 2.0
   - Projet militant open-source: GPL v3
   
   Où trouver le texte?
   - MIT: https://choosealicense.com/licenses/mit/
   - Apache 2.0: https://choosealicense.com/licenses/apache-2.0/
   - GPL v3: https://choosealicense.com/licenses/gpl-3.0/


═══════════════════════════════════════════════════════════════════
FICHIERS FORTEMENT RECOMMANDÉS - PROJET PROFESSIONNEL
═══════════════════════════════════════════════════════════════════

[6] CHANGELOG.md
────────────────────────────────────────────────────────────────────
   - Fichier Markdown listant TOUS les changements entre chaque version
   - Permet aux utilisateurs de savoir ce qui a changé avant de mettre à jour
   - Format standard: keepachangelog.com
   - Sections par version: [version] - date, puis Added/Changed/Deprecated/Removed/Fixed/Security
   - OBLIGATOIRE pour un projet professionnel sérieux
   - Mettre à jour à CHAQUE nouvelle version publiée
   
   Pourquoi c'est important?
   - Les utilisateurs savent si la mise à jour cassera leur code
   - Montre que le projet est activement maintenu
   - Facilite le débogage (savoir quand un bug est apparu)
   - Transparence sur l'évolution du projet
   
   Format recommandé (keepachangelog.com):
   # Changelog
   
   All notable changes to this project will be documented in this file.
   
   ## [Unreleased]
   ### Added
   - Nouvelle fonctionnalité en cours de dev
   
   ## [1.1.0] - 2025-11-15
   ### Added
   - Nouvelle fonction `process_data()`
   - Support pour Python 3.12
   
   ### Changed
   - Amélioration des performances de `calculate()`
   
   ### Fixed
   - Correction du bug #123 dans la fonction `parse()`
   
   ## [1.0.0] - 2025-10-01
   ### Added
   - Version initiale
   - Fonctions de base: `ma_fonction()`, `MaClasse`


[7] MANIFEST.in
────────────────────────────────────────────────────────────────────
   - Fichier texte qui liste les fichiers NON-PYTHON à inclure dans votre package
   - Par défaut, seuls les fichiers .py sont inclus lors de la distribution
   - Permet d'ajouter: README, LICENSE, images, données JSON/CSV, templates HTML...
   - Format: Une instruction par ligne (include, exclude, recursive-include, etc.)
   - S'exécute lors de la création du package avec "python -m build"
   - NÉCESSAIRE dès que vous avez des fichiers non-.py à distribuer
   
   Commandes disponibles:
   - include: inclure des fichiers spécifiques
   - exclude: exclure des fichiers spécifiques
   - recursive-include: inclure des patterns dans tous les sous-dossiers
   - recursive-exclude: exclure des patterns dans tous les sous-dossiers
   - global-include: inclure dans tout le projet
   - global-exclude: exclure de tout le projet
   - graft: inclure tout un dossier
   - prune: exclure tout un dossier
   
   Exemple complet:
   # Inclure la documentation
   include README.md
   include LICENSE
   include CHANGELOG.md
   
   # Inclure des fichiers de données
   recursive-include monmodule/data *.json
   recursive-include monmodule/templates *.html *.css
   recursive-include monmodule/static *.png *.jpg
   
   # Exclure des fichiers inutiles
   global-exclude *.pyc
   global-exclude *.pyo
   global-exclude __pycache__
   global-exclude .DS_Store
   global-exclude *.so
   
   # Exclure les tests du package distribué
   prune tests
   prune docs


[8] .gitignore
────────────────────────────────────────────────────────────────────
   - Fichier texte listant les fichiers/dossiers à NE PAS versionner avec Git
   - Évite de commiter: caches Python, environnements virtuels, builds, secrets
   - Format: Un pattern par ligne (*.pyc, __pycache__/, venv/, etc.)
   - Git ignore automatiquement tout ce qui matche ces patterns
   - OBLIGATOIRE dès que vous utilisez Git
   - Premier fichier à créer après "git init"
   
   Pourquoi c'est crucial?
   - Garde le dépôt propre (pas de fichiers temporaires)
   - Réduit drastiquement la taille du dépôt
   - Évite de partager des secrets (.env, API keys, credentials)
   - Évite les conflits Git (chaque dev a des caches différents)
   - Accélère les opérations Git
   
   Catégories à toujours ignorer:
   
   # Bytecode Python
   __pycache__/
   *.py[cod]
   *$py.class
   *.so
   
   # Distribution / packaging
   dist/
   build/
   *.egg-info/
   *.egg
   
   # Environnements virtuels
   venv/
   env/
   ENV/
   .venv/
   
   # Tests
   .pytest_cache/
   .coverage
   htmlcov/
   .tox/
   
   # IDE
   .vscode/
   .idea/
   *.swp
   *.swo
   *~
   
   # OS
   .DS_Store
   Thumbs.db
   
   # Secrets (IMPORTANT!)
   .env
   .env.local
   secrets.json
   *.pem
   *.key


[9] tests/ (dossier)
────────────────────────────────────────────────────────────────────
   - Dossier contenant tous vos tests unitaires
   - Essentiel pour vérifier que votre code fonctionne
   - Convention: nommer les fichiers test_*.py
   - Utilise généralement pytest (framework de tests Python)
   - FORTEMENT RECOMMANDÉ - un module sans tests est suspect
   
   Structure typique:
   tests/
   ├── __init__.py              # Optionnel mais recommandé
   ├── conftest.py              # Fixtures partagées
   ├── test_core.py             # Tests pour core.py
   ├── test_utils.py            # Tests pour utils.py
   └── test_integration.py      # Tests d'intégration
   
   Exemple de test simple:
   # tests/test_core.py
   from monmodule.core import ma_fonction
   
   def test_ma_fonction():
       """Teste que ma_fonction additionne correctement"""
       assert ma_fonction(2, 3) == 5
       assert ma_fonction(0, 0) == 0
       assert ma_fonction(-1, 1) == 0
   
   Lancer les tests:
   pytest                    # Tous les tests
   pytest tests/test_core.py # Un fichier spécifique
   pytest -v                 # Mode verbeux
   pytest --cov              # Avec couverture


═══════════════════════════════════════════════════════════════════
FICHIERS DE CONFIGURATION - DISTRIBUTION
═══════════════════════════════════════════════════════════════════

[10] .pre-commit-config.yaml
────────────────────────────────────────────────────────────────────
   - Configuration pour l'outil "pre-commit" (système de hooks Git)
   - Exécute automatiquement des vérifications AVANT chaque commit Git
   - Format YAML listant les outils à exécuter (black, flake8, tests...)
   - Si une vérification échoue, le commit est automatiquement bloqué
   - TRÈS UTILE pour maintenir la qualité du code automatiquement
   - Évite d'oublier de formatter/tester avant de commiter
   
   ═══════════════════════════════════════════════════════════════
   QU'EST-CE QUE PRE-COMMIT ?
   ═══════════════════════════════════════════════════════════════
   
   pre-commit est un outil Python qui permet d'exécuter automatiquement
   des scripts (appelés "hooks") AVANT que Git n'enregistre un commit.
   
   ANALOGIE SIMPLE:
   C'est comme un contrôle de sécurité à l'aéroport:
   - Vous voulez embarquer (= faire un commit)
   - Le contrôle vérifie votre bagage (= votre code)
   - Si tout est OK -> vous passez (commit réussi)
   - Si problème détecté -> vous devez corriger (commit bloqué)
   
   ═══════════════════════════════════════════════════════════════
   INSTALLATION ET ACTIVATION (ÉTAPE PAR ÉTAPE)
   ═══════════════════════════════════════════════════════════════
   
   Étape 1: Installer l'outil pre-commit
   ─────────────────────────────────────
   pip install pre-commit
   
   Étape 2: Créer le fichier .pre-commit-config.yaml
   ──────────────────────────────────────────────────
   # À la racine de votre projet, créer ce fichier
   # (voir exemples plus bas)
   
   Étape 3: Activer pre-commit dans votre dépôt Git
   ─────────────────────────────────────────────────
   pre-commit install
   
   # Cette commande installe les hooks dans .git/hooks/
   # À partir de maintenant, pre-commit s'exécutera automatiquement
   # à chaque "git commit"
   
   Étape 4: (Optionnel) Tester manuellement
   ─────────────────────────────────────────
   pre-commit run --all-files
   # Lance tous les hooks sur tous les fichiers
   # Utile pour vérifier la configuration
   
   ═══════════════════════════════════════════════════════════════
   COMMENT ÇA MARCHE EN PRATIQUE (SCÉNARIO RÉEL)
   ═══════════════════════════════════════════════════════════════
   
   SANS pre-commit (situation classique):
   ──────────────────────────────────────
   1. Vous écrivez du code Python mal formaté
   2. git add fichier.py
   3. git commit -m "ajout fonction"
   4. Commit réussi [OK]
   5. Vous poussez sur GitHub
   6. La CI/CD détecte des erreurs de format
   7. Vous devez refaire un commit pour corriger
   8. Temps perdu, historique Git pollué
   
   AVEC pre-commit (automatique):
   ─────────────────────────────
   1. Vous écrivez du code Python mal formaté
   2. git add fichier.py
   3. git commit -m "ajout fonction"
   4. [ATTENTION]  pre-commit s'exécute automatiquement:
      
      Check for added large files...........................Passed
      Fix End of Files.....................................Failed
      - hook id: end-of-file-fixer
      - files were modified by this hook
      
      Fixing fichier.py
      
      black................................................Failed
      - hook id: black
      - files were modified by this hook
      
      reformatted fichier.py
      All done! * [SHORTCAKE] *
      1 file reformatted.
   
   5. [X] Le commit est BLOQUÉ
   6. Les fichiers ont été AUTOMATIQUEMENT corrigés par black
   7. Vous refaites:
      git add fichier.py
      git commit -m "ajout fonction"
   8. [OK] Cette fois tout passe, commit réussi!
   
   Résultat: Vous commitez toujours du code propre!
   
   ═══════════════════════════════════════════════════════════════
   STRUCTURE DU FICHIER .pre-commit-config.yaml
   ═══════════════════════════════════════════════════════════════
   
   Le fichier est en format YAML avec cette structure:
   
   repos:                          # Liste des dépôts de hooks
     - repo: URL_DU_DEPOT         # URL d'un dépôt de hooks
       rev: VERSION               # Version du hook à utiliser
       hooks:                     # Liste des hooks de ce dépôt
         - id: NOM_DU_HOOK       # Identifiant du hook
           args: [...]           # Arguments optionnels
           files: ...            # Pattern de fichiers (optionnel)
   
   Chaque "repo" est un dépôt GitHub contenant des hooks préfabriqués.
   
   ═══════════════════════════════════════════════════════════════
   EXEMPLE 1: CONFIGURATION MINIMALE (DÉBUTANT)
   ═══════════════════════════════════════════════════════════════
   
   # .pre-commit-config.yaml
   repos:
     # Hooks basiques fournis par pre-commit
     - repo: https://github.com/pre-commit/pre-commit-hooks
       rev: v4.5.0
       hooks:
         - id: trailing-whitespace    # Supprime espaces en fin de ligne
         - id: end-of-file-fixer      # Ajoute newline en fin de fichier
         - id: check-yaml             # Vérifie syntaxe YAML
         - id: check-added-large-files # Bloque fichiers > 500KB
   
     # Black: formateur de code Python
     - repo: https://github.com/psf/black
       rev: 23.10.0
       hooks:
         - id: black
           language_version: python3.11
   
   QUE FAIT CETTE CONFIG?
   ─────────────────────
   À chaque commit, vérifie:
   1. Pas d'espaces inutiles en fin de ligne
   2. Fichiers se terminent par une ligne vide
   3. Fichiers YAML sont valides
   4. Aucun gros fichier accidentellement ajouté
   5. Code Python formaté selon black
   
   ═══════════════════════════════════════════════════════════════
   EXEMPLE 2: CONFIGURATION COMPLÈTE (PROFESSIONNEL)
   ═══════════════════════════════════════════════════════════════
   
   # .pre-commit-config.yaml
   repos:
     # 1. Hooks généraux (nettoyage, vérifications basiques)
     - repo: https://github.com/pre-commit/pre-commit-hooks
       rev: v4.5.0
       hooks:
         - id: trailing-whitespace      # Espaces en fin de ligne
         - id: end-of-file-fixer        # Newline finale
         - id: check-yaml               # Syntaxe YAML
         - id: check-json               # Syntaxe JSON
         - id: check-toml               # Syntaxe TOML
         - id: check-added-large-files  # Fichiers trop gros
           args: ['--maxkb=500']
         - id: check-merge-conflict     # Marqueurs de conflit Git
         - id: debug-statements         # print(), pdb, etc.
         - id: mixed-line-ending        # Fins de ligne cohérentes
   
     # 2. Black: Formatage automatique du code
     - repo: https://github.com/psf/black
       rev: 23.10.0
       hooks:
         - id: black
           language_version: python3.11
           args: ['--line-length=88']
   
     # 3. isort: Trie les imports alphabétiquement
     - repo: https://github.com/pycqa/isort
       rev: 5.12.0
       hooks:
         - id: isort
           args: ['--profile', 'black']  # Compatible avec black
   
     # 4. Flake8: Vérification du style (PEP 8)
     - repo: https://github.com/pycqa/flake8
       rev: 6.1.0
       hooks:
         - id: flake8
           args: ['--max-line-length=88', '--extend-ignore=E203,W503']
   
     # 5. mypy: Vérification des types
     - repo: https://github.com/pre-commit/mirrors-mypy
       rev: v1.7.0
       hooks:
         - id: mypy
           additional_dependencies: [types-requests]
           args: ['--ignore-missing-imports']
   
     # 6. Tests: Lance pytest avant chaque commit
     - repo: local
       hooks:
         - id: pytest
           name: pytest
           entry: pytest
           language: system
           pass_filenames: false
           always_run: true
   
   ATTENTION: Cette config est TRÈS stricte!
   Elle lance black, isort, flake8, mypy ET pytest à chaque commit.
   Cela peut ralentir vos commits. Adaptez selon vos besoins.
   
   ═══════════════════════════════════════════════════════════════
   EXEMPLE 3: CONFIGURATION ÉQUILIBRÉE (RECOMMANDÉ)
   ═══════════════════════════════════════════════════════════════
   
   # .pre-commit-config.yaml
   repos:
     # Vérifications rapides et essentielles
     - repo: https://github.com/pre-commit/pre-commit-hooks
       rev: v4.5.0
       hooks:
         - id: trailing-whitespace
         - id: end-of-file-fixer
         - id: check-yaml
         - id: check-added-large-files
         - id: debug-statements        # Bloque print() oubliés
   
     # Formatage automatique (rapide)
     - repo: https://github.com/psf/black
       rev: 23.10.0
       hooks:
         - id: black
   
     # Vérification style (moyen)
     - repo: https://github.com/pycqa/flake8
       rev: 6.1.0
       hooks:
         - id: flake8
           args: ['--max-line-length=88']
   
   # Note: Pas de tests ici, trop lent pour chaque commit
   # Les tests sont dans la CI/CD (GitHub Actions)
   
   ═══════════════════════════════════════════════════════════════
   HOOKS POPULAIRES ET LEUR UTILITÉ
   ═══════════════════════════════════════════════════════════════
   
   FORMATAGE (modifient automatiquement le code):
   ──────────────────────────────────────────────
   black             -> Formate Python (standard de facto)
   isort             -> Trie les imports
   autopep8          -> Corrige erreurs PEP 8 automatiquement
   prettier          -> Formate JSON, YAML, Markdown
   
   LINTING (détectent des problèmes):
   ──────────────────────────────────
   flake8            -> Vérifie style Python (PEP 8)
   pylint            -> Analyse statique approfondie
   mypy              -> Vérifie les types
   bandit            -> Détecte failles de sécurité
   
   VÉRIFICATIONS GÉNÉRALES:
   ────────────────────────
   trailing-whitespace    -> Supprime espaces inutiles
   end-of-file-fixer      -> Ajoute newline finale
   check-yaml             -> Vérifie syntaxe YAML
   check-json             -> Vérifie syntaxe JSON
   check-merge-conflict   -> Détecte conflits Git non résolus
   debug-statements       -> Bloque print(), pdb.set_trace()
   check-added-large-files -> Bloque gros fichiers (>500KB)
   
   ═══════════════════════════════════════════════════════════════
   COMMANDES UTILES
   ═══════════════════════════════════════════════════════════════
   
   # Installer pre-commit
   pip install pre-commit
   
   # Activer dans le dépôt Git
   pre-commit install
   
   # Désactiver temporairement
   pre-commit uninstall
   
   # Lancer manuellement sur tous les fichiers
   pre-commit run --all-files
   
   # Lancer un hook spécifique
   pre-commit run black --all-files
   
   # Mettre à jour les versions des hooks
   pre-commit autoupdate
   
   # Contourner pre-commit pour UN commit (urgence)
   git commit -m "message" --no-verify
   # [ATTENTION]  À utiliser EXCEPTIONNELLEMENT
   
   ═══════════════════════════════════════════════════════════════
   AVANTAGES ET INCONVÉNIENTS
   ═══════════════════════════════════════════════════════════════
   
   [OK] AVANTAGES:
   ─────────────
   - Code toujours propre et formaté
   - Détection immédiate des erreurs
   - Impossible d'oublier de lancer black/flake8
   - Uniformité du code dans toute l'équipe
   - Moins de commentaires en code review
   - Historique Git propre
   - Gratuit et facile à configurer
   
   [X] INCONVÉNIENTS:
   ─────────────────
   - Ralentit légèrement les commits (quelques secondes)
   - Peut être frustrant au début (commits bloqués)
   - Nécessite que tous les devs installent pre-commit
   - Configuration initiale peut être complexe
   
   [IDEE] CONSEIL: Commencez avec une config simple (black uniquement)
   puis ajoutez progressivement d'autres hooks selon vos besoins.
   
   ═══════════════════════════════════════════════════════════════
   QUAND L'UTILISER?
   ═══════════════════════════════════════════════════════════════
   
   [OK] UTILISER SI:
   - Vous travaillez en équipe (uniformité du code)
   - Vous oubliez souvent de formatter
   - Vous voulez forcer la qualité du code
   - Vous avez une CI/CD qui vérifie le code
   
   [X] PAS NÉCESSAIRE SI:
   - Projet solo et vous êtes discipliné
   - Débutant absolu (peut être décourageant)
   - Prototypage rapide (trop strict)
   
   [OBJECTIF] RECOMMANDATION:
   Pour un projet professionnel: OUI, absolument!
   Pour apprendre: Optionnel, ajoutez plus tard


═══════════════════════════════════════════════════════════════════
FICHIERS DE DÉPENDANCES
═══════════════════════════════════════════════════════════════════

[11] requirements.txt
────────────────────────────────────────────────────────────────────
   - Liste simple des dépendances Python nécessaires
   - Format: Une dépendance par ligne avec version optionnelle
   - Utilisé avec: pip install -r requirements.txt
   - NE PAS CONFONDRE avec les dépendances dans pyproject.toml!
   
   DIFFÉRENCE CRITIQUE (source de confusion fréquente):
   
   pyproject.toml [project.dependencies]:
   - Dépendances pour UTILISER votre module (runtime dependencies)
   - Ce dont votre code a besoin pour fonctionner
   - Installées automatiquement avec: pip install monmodule
   - Exemple: requests, numpy, pandas
   
   requirements.txt:
   - Dépendances pour DÉVELOPPER/TESTER votre module (dev dependencies)
   - Outils que VOUS utilisez, pas les utilisateurs finaux
   - Installées manuellement avec: pip install -r requirements.txt
   - Exemple: pytest, black, flake8, mypy
   
   Analogie:
   - pyproject.toml = ingrédients pour faire un gâteau
   - requirements.txt = ustensiles de cuisine
   
   Exemple requirements.txt:
   pytest>=7.0.0
   pytest-cov>=4.0.0
   black==23.10.0
   flake8>=6.0.0
   mypy>=1.0.0


[12] requirements-dev.txt
────────────────────────────────────────────────────────────────────
   - Variante de requirements.txt SPÉCIFIQUEMENT pour le développement
   - Contient: outils de test, formatage, documentation, type-checking
   - Séparé de requirements.txt pour plus de clarté
   - Installation: pip install -r requirements-dev.txt
   - Les utilisateurs finaux n'ont PAS besoin de ces dépendances
   
   Différence avec requirements.txt:
   - requirements.txt: dépendances de base (pytest, etc.)
   - requirements-dev.txt: dépendances avancées (sphinx, pre-commit...)
   
   Contenu typique:
   # Tests
   pytest>=7.0.0
   pytest-cov>=4.0.0
   pytest-xdist>=3.0.0        # Tests parallèles
   
   # Qualité du code
   black>=23.0.0              # Formatage
   flake8>=6.0.0              # Linting
   mypy>=1.0.0                # Type checking
   pylint>=3.0.0              # Analyse statique
   
   # Documentation
   sphinx>=6.0.0
   sphinx-rtd-theme>=1.3.0
   
   # Publication
   build>=1.0.0               # Construire le package
   twine>=4.0.0               # Publier sur PyPI
   
   # Outils
   pre-commit>=3.0.0
   tox>=4.0.0


═══════════════════════════════════════════════════════════════════
FICHIERS DE CONFIGURATION - TESTS
═══════════════════════════════════════════════════════════════════

[13] pytest.ini
────────────────────────────────────────────────────────────────────
   - Fichier de configuration pour pytest (framework de tests)
   - Format INI, section [pytest] ou [tool:pytest]
   - Configure: où sont les tests, quels fichiers tester, options par défaut
   - Alternative moderne: section [tool.pytest.ini_options] dans pyproject.toml
   - OPTIONNEL: pytest fonctionne parfaitement avec ses valeurs par défaut
   - À utiliser si vous voulez personnaliser pytest
   
   Configurations courantes et leur utilité:
   
   - testpaths: où chercher les tests
     Défaut: tout le projet (lent)
     Recommandé: tests/ uniquement
   
   - python_files: pattern des fichiers de test
     Défaut: test_*.py et *_test.py
     Parfois: test_*.py uniquement
   
   - python_classes: pattern des classes de test
     Défaut: Test*
   
   - python_functions: pattern des fonctions de test
     Défaut: test_*
   
   - addopts: options passées automatiquement à pytest
     Utile pour: -v (verbeux), --cov (couverture), etc.
   
   Exemple complet:
   [pytest]
   testpaths = tests
   python_files = test_*.py
   python_classes = Test*
   python_functions = test_*
   addopts = -v --cov=monmodule --cov-report=html
   
   Ou dans pyproject.toml (recommandé):
   [tool.pytest.ini_options]
   testpaths = ["tests"]
   python_files = "test_*.py"
   addopts = "-v --cov=monmodule"


[14] conftest.py
────────────────────────────────────────────────────────────────────
   - Fichier spécial pytest pour partager des fixtures entre tests
   - Placé dans le dossier tests/
   - Pas besoin de l'importer, pytest le charge automatiquement
   - Contient: fixtures (données de test), hooks, configuration partagée
   - TRÈS UTILE pour éviter la duplication de code dans les tests
   
   Qu'est-ce qu'une fixture?
   - Fonction qui prépare des données/objets pour les tests
   - Réutilisable dans plusieurs tests
   - Se "nettoie" automatiquement après chaque test
   
   Exemple complet:
   # tests/conftest.py
   import pytest
   import tempfile
   from pathlib import Path
   
   @pytest.fixture
   def sample_data():
       """Données de test simples"""
       return {"name": "Test", "value": 42}
   
   @pytest.fixture
   def temp_file():
       """Crée un fichier temporaire, le supprime après"""
       with tempfile.NamedTemporaryFile(mode='w', delete=False) as f:
           f.write("test content")
           temp_path = f.name
       
       yield temp_path  # Le test utilise ce fichier
       
       # Cleanup automatique après le test
       Path(temp_path).unlink()
   
   # Utilisation dans un test:
   # tests/test_core.py
   def test_with_fixture(sample_data):
       assert sample_data["value"] == 42
   
   def test_with_temp_file(temp_file):
       content = Path(temp_file).read_text()
       assert content == "test content"


[15] tox.ini
────────────────────────────────────────────────────────────────────
   - Configuration pour Tox (outil de test multi-environnements)
   - Permet de tester votre code sur plusieurs versions Python AUTOMATIQUEMENT
   - Crée des environnements virtuels isolés pour chaque version
   - Format INI avec sections [tox] et [testenv]
   - TRÈS UTILE pour vérifier la compatibilité Python 3.8, 3.9, 3.10, 3.11, 3.12
   - NÉCESSAIRE si vous supportez plusieurs versions Python
   
   Problème résolu:
   - Votre code fonctionne sur Python 3.11 (votre machine)
   - Mais un utilisateur a Python 3.8
   - Votre code plante chez lui
   - Tox teste automatiquement sur TOUTES les versions
   
   Comment ça marche:
   1. Vous lancez: tox
   2. Tox lit tox.ini
   3. Pour chaque version listée:
      - Crée un venv propre
      - Installe votre package
      - Installe les dépendances
      - Lance les tests
   4. Affiche un résumé: [OK] py38, [OK] py39, [X] py310
   
   Exemple minimal:
   [tox]
   envlist = py38,py39,py310,py311,py312
   
   [testenv]
   deps = pytest
   commands = pytest tests/
   
   Exemple avancé:
   [tox]
   envlist = py{38,39,310,311,312}
   isolated_build = True
   
   [testenv]
   deps =
       pytest>=7.0
       pytest-cov>=4.0
   commands =
       pytest --cov=monmodule tests/
   
   [testenv:lint]
   deps = flake8
   commands = flake8 monmodule/
   
   [testenv:type]
   deps = mypy
   commands = mypy monmodule/


═══════════════════════════════════════════════════════════════════
FICHIERS DE TYPE HINTS (ANNOTATIONS DE TYPES)
═══════════════════════════════════════════════════════════════════

[16] py.typed
────────────────────────────────────────────────────────────────────
   - Fichier COMPLÈTEMENT VIDE (0 octets) placé dans votre package
   - Signale que votre package contient des annotations de types (type hints)
   - Permet aux outils comme mypy de vérifier les types chez vos utilisateurs
   - Défini par PEP 561 (2017)
   - OBLIGATOIRE si vous utilisez des type hints ET voulez qu'ils soient utiles
   
   Problème sans py.typed:
   
   # Votre code (monmodule/core.py)
   def add(a: int, b: int) -> int:
       return a + b
   
   # Un utilisateur de votre module
   from monmodule import add
   result = add("hello", "world")  # Erreur de type!
   # mypy NE VERRA PAS l'erreur car pas de py.typed
   
   Avec py.typed:
   
   # mypy détecte l'erreur:
   # error: Argument 1 to "add" has incompatible type "str"; expected "int"
   
   Comment créer:
   
   1. Créer le fichier vide:
      touch monmodule/py.typed
      # ou sur Windows:
      type nul > monmodule\py.typed
   
   2. L'inclure dans le package (pyproject.toml):
      [tool.setuptools.package-data]
      monmodule = ["py.typed"]
   
   3. Ou dans MANIFEST.in:
      include monmodule/py.typed
   
   Quand l'utiliser:
   - Vous utilisez des type hints dans votre code
   - Vous voulez que les IDE aident vos utilisateurs
   - Vous voulez que mypy détecte les erreurs chez vos utilisateurs


[17] mypy.ini ou section [mypy] dans pyproject.toml
────────────────────────────────────────────────────────────────────
   - Configuration pour mypy (vérificateur de types statique)
   - Format INI ou section TOML dans pyproject.toml
   - Configure: niveau de strictness, modules à ignorer, options
   - OPTIONNEL: mypy fonctionne avec des valeurs par défaut raisonnables
   - À utiliser si vous utilisez des type hints sérieusement
   
   Qu'est-ce que mypy?
   - Outil qui vérifie les types SANS exécuter le code
   - Détecte: mauvais types, None non géré, attributs inexistants...
   - Comme un compilateur pour les types Python
   
   Options courantes:
   
   - python_version: version Python ciblée
   - warn_return_any: avertir si fonction retourne Any
   - warn_unused_ignores: avertir si # type: ignore inutile
   - strict: active TOUS les checks stricts (recommandé!)
   - ignore_missing_imports: ignore erreurs des libs sans types
   
   Exemple mypy.ini:
   [mypy]
   python_version = 3.8
   warn_return_any = True
   warn_unused_configs = True
   disallow_untyped_defs = True
   
   [mypy-tests.*]
   ignore_errors = True
   
   Exemple dans pyproject.toml (recommandé):
   [tool.mypy]
   python_version = "3.8"
   warn_return_any = true
   strict = true
   
   [[tool.mypy.overrides]]
   module = "tests.*"
   ignore_errors = true


═══════════════════════════════════════════════════════════════════
FICHIERS DE DOCUMENTATION
═══════════════════════════════════════════════════════════════════

[18] docs/conf.py
────────────────────────────────────────────────────────────────────
   - Configuration pour Sphinx (générateur de documentation HTML)
   - Fichier Python avec variables de configuration
   - Définit: thème visuel, extensions, chemins, métadonnées du projet
   - Créé automatiquement par: sphinx-quickstart
   - OBLIGATOIRE si vous utilisez Sphinx pour générer la documentation
   
   Qu'est-ce que Sphinx?
   - Outil qui génère une belle documentation HTML à partir de:
     * Fichiers .rst (reStructuredText)
     * Docstrings de votre code Python
   - Utilisé par: Python officiel, Django, NumPy, etc.
   - Permet d'héberger sur Read the Docs gratuitement
   
   Contenu typique:
   # docs/conf.py
   import os
   import sys
   sys.path.insert(0, os.path.abspath('..'))
   
   project = 'Mon Module'
   copyright = '2025, Votre Nom'
   author = 'Votre Nom'
   version = '1.0'
   release = '1.0.0'
   
   extensions = [
       'sphinx.ext.autodoc',      # Docs depuis docstrings
       'sphinx.ext.napoleon',     # Support Google/NumPy style
       'sphinx.ext.viewcode',     # Liens vers le code
   ]
   
   templates_path = ['_templates']
   exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
   
   html_theme = 'sphinx_rtd_theme'  # Thème Read the Docs
   html_static_path = ['_static']


[19] docs/index.rst
────────────────────────────────────────────────────────────────────
   - Page d'accueil de votre documentation Sphinx
   - Format reStructuredText (.rst, comme Markdown mais plus puissant)
   - Contient la table des matières (toctree) liant toutes les pages
   - Point d'entrée de toute la documentation
   - OBLIGATOIRE si vous utilisez Sphinx
   
   Qu'est-ce que reStructuredText (rst)?
   - Format de markup comme Markdown
   - Plus puissant: tables complexes, notes, warnings, etc.
   - Standard pour la documentation Python
   
   Exemple minimal:
   Mon Module
   ==========
   
   Bienvenue dans la documentation de Mon Module!
   
   .. toctree::
      :maxdepth: 2
      :caption: Contenu:
   
      installation
      usage
      api
      contributing
   
   Installation
   ------------
   
   .. code-block:: bash
   
      pip install monmodule
   
   Exemple d'utilisation
   ---------------------
   
   .. code-block:: python
   
      from monmodule import ma_fonction
      result = ma_fonction(2, 3)
   
   Génération de la doc:
   cd docs/
   make html
   # Ouvrir docs/_build/html/index.html


═══════════════════════════════════════════════════════════════════
FICHIERS CI/CD (INTÉGRATION CONTINUE)
═══════════════════════════════════════════════════════════════════

[20] .github/workflows/*.yml
────────────────────────────────────────────────────────────────────
   - Configuration pour GitHub Actions (CI/CD gratuit sur GitHub)
   - Format YAML décrivant les actions automatiques à exécuter
   - Peut: lancer tests, publier sur PyPI, générer docs, vérifier code...
   - S'exécute automatiquement à chaque push, pull request, tag, etc.
   - TRÈS UTILE pour automatiser qualité et déploiement
   - Gratuit pour projets open-source
   
   Qu'est-ce que CI/CD?
   - Continuous Integration: tests automatiques à chaque commit
   - Continuous Deployment: publication automatique sur PyPI
   - Garantit que le code fonctionne toujours
   
   Workflows typiques:
   
   1. Tests (.github/workflows/tests.yml):
   name: Tests
   
   on:
     push:
       branches: [main]
     pull_request:
       branches: [main]
   
   jobs:
     test:
       runs-on: ubuntu-latest
       strategy:
         matrix:
           python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
       
       steps:
         - uses: actions/checkout@v3
         
         - name: Set up Python ${{ matrix.python-version }}
           uses: actions/setup-python@v4
           with:
             python-version: ${{ matrix.python-version }}
         
         - name: Install dependencies
           run: |
             pip install -e .[dev]
             pip install pytest pytest-cov
         
         - name: Run tests
           run: pytest --cov
   
   2. Publication PyPI (.github/workflows/publish.yml):
   name: Publish to PyPI
   
   on:
     release:
       types: [published]
   
   jobs:
     deploy:
       runs-on: ubuntu-latest
       steps:
         - uses: actions/checkout@v3
         - uses: actions/setup-python@v4
         - name: Build and publish
           env:
             TWINE_USERNAME: __token__
             TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
           run: |
             pip install build twine
             python -m build
             twine upload dist/*


═══════════════════════════════════════════════════════════════════
FICHIERS DE QUALITÉ ET STANDARDS
═══════════════════════════════════════════════════════════════════

[21] .editorconfig
────────────────────────────────────────────────────────────────────
   - Configuration pour harmoniser le style de code entre éditeurs/IDE
   - Format INI simple, lisible par VS Code, PyCharm, Sublime, etc.
   - Définit: taille indentation, charset, fin de ligne, espaces vs tabs
   - Garantit cohérence même si les devs utilisent différents éditeurs
   - OPTIONNEL mais très utile en équipe
   
   Problème résolu:
   - Dev A utilise VS Code (4 espaces)
   - Dev B utilise PyCharm (2 espaces)
   - Dev C utilise Vim (tabs)
   - Le code devient inconsistant et illisible
   - .editorconfig force les mêmes règles pour tous
   
   Exemple complet:
   # .editorconfig
   root = true
   
   [*]
   charset = utf-8
   end_of_line = lf
   insert_final_newline = true
   trim_trailing_whitespace = true
   
   [*.py]
   indent_style = space
   indent_size = 4
   max_line_length = 88
   
   [*.{yml,yaml}]
   indent_size = 2
   
   [*.md]
   trim_trailing_whitespace = false
   
   [Makefile]
   indent_style = tab


[22] SECURITY.md
────────────────────────────────────────────────────────────────────
   - Instructions pour signaler des failles de sécurité de manière responsable
   - Format Markdown
   - Explique: comment rapporter, délais de réponse, politique de divulgation
   - Important pour projets utilisés en production
   - GitHub affiche automatiquement un bouton "Report a vulnerability"
   
   Pourquoi c'est important?
   - Les chercheurs en sécurité savent comment vous contacter
   - Évite la divulgation publique immédiate (temps de corriger)
   - Montre le professionnalisme du projet
   
   Exemple:
   # Security Policy
   
   ## Supported Versions
   
   | Version | Supported          |
   | ------- | ------------------ |
   | 1.1.x   | :white_check_mark: |
   | 1.0.x   | :x:                |
   | < 1.0   | :x:                |
   
   ## Reporting a Vulnerability
   
   **Please do not report security vulnerabilities through public GitHub issues.**
   
   Instead, please email security@example.com with:
   - Type of issue (e.g. buffer overflow, SQL injection, etc.)
   - Full paths of source file(s) related to the issue
   - Location of the affected source code (tag/branch/commit)
   - Step-by-step instructions to reproduce the issue
   - Proof-of-concept or exploit code (if possible)
   - Impact of the issue
   
   We will respond within 48 hours and provide a detailed response within 7 days.


[23] CONTRIBUTING.md
────────────────────────────────────────────────────────────────────
   - Guide pour les contributeurs externes qui veulent aider
   - Format Markdown
   - Explique: comment contribuer, standards de code, processus PR, code of conduct
   - Réduit les pull requests de mauvaise qualité
   - Montre que le projet accepte et encourage les contributions
   
   Sections typiques:
   
   # Contributing to Mon Module
   
   ## Code of Conduct
   This project follows the Contributor Covenant Code of Conduct.
   
   ## How to Contribute
   
   ### Reporting Bugs
   - Use GitHub Issues
   - Include: Python version, OS, error message, minimal example
   
   ### Suggesting Features
   - Open an issue first to discuss
   - Explain the use case and benefits
   
   ### Pull Requests
   1. Fork the repo
   2. Create a feature branch: `git checkout -b feature-name`
   3. Make your changes
   4. Add tests for your changes
   5. Run: `pytest`, `black .`, `flake8`
   6. Commit: `git commit -m "Add feature X"`
   7. Push: `git push origin feature-name`
   8. Open a Pull Request
   
   ## Development Setup
   ```bash
   git clone https://github.com/user/monmodule.git
   cd monmodule
   python -m venv venv
   source venv/bin/activate
   pip install -e .[dev]
   pre-commit install
   ```
   
   ## Coding Standards
   - Follow PEP 8
   - Use type hints
   - Write docstrings (Google style)
   - Test coverage > 80%
   - Format with black
   - Lint with flake8


═══════════════════════════════════════════════════════════════════
FICHIERS LEGACY (À ÉVITER SUR NOUVEAUX PROJETS)
═══════════════════════════════════════════════════════════════════

[24] setup.py
────────────────────────────────────────────────────────────────────
   - ANCIEN fichier de configuration Python (avant pyproject.toml)
   - Script Python qui appelle setuptools.setup() avec les métadonnées
   - Contenait: nom, version, dépendances, auteur, description, etc.
   - MAINTENANT OBSOLÈTE: Remplacé par pyproject.toml depuis 2016 (PEP 518)
   - Encore présent dans beaucoup de vieux projets (2010-2020)
   - NE PAS UTILISER pour nouveaux projets
   
   Pourquoi c'est obsolète?
   - Exécute du code Python arbitraire (risque de sécurité)
   - Difficile à parser automatiquement par les outils
   - Mélange configuration déclarative et code impératif
   - Pas de lock de dépendances
   - Remplacé par pyproject.toml (standard moderne)
   
   Exemple (pour référence historique):
   # setup.py (NE PAS UTILISER)
   from setuptools import setup, find_packages
   
   setup(
       name="monmodule",
       version="1.0.0",
       author="Votre Nom",
       author_email="email@example.com",
       description="Description courte",
       long_description=open("README.md").read(),
       long_description_content_type="text/markdown",
       url="https://github.com/user/monmodule",
       packages=find_packages(),
       classifiers=[
           "Programming Language :: Python :: 3",
           "License :: OSI Approved :: MIT License",
       ],
       python_requires=">=3.8",
       install_requires=[
           "requests>=2.28.0",
       ],
   )
   
   Quand vous pourriez encore le voir:
   - Vieux projets non maintenus
   - Projets avec build complexe (extensions C)
   - Migration en cours vers pyproject.toml


[25] setup.cfg
────────────────────────────────────────────────────────────────────
   - Fichier de configuration au format INI (comme un .ini Windows)
   - Alternative ou complément à setup.py
   - Plus sûr que setup.py (pas d'exécution de code arbitraire)
   - Pouvait configurer: setuptools, pytest, flake8, mypy, etc.
   - MAINTENANT MOINS UTILISÉ: pyproject.toml fait la même chose mieux
   - Quelques outils anciens le nécessitent encore
   
   Pourquoi c'est dépassé?
   - pyproject.toml est le nouveau standard (PEP 517/518/621)
   - Format TOML plus moderne et lisible que INI
   - Tous les nouveaux outils supportent pyproject.toml
   - Fragmentation: config éparpillée entre setup.cfg, setup.py, etc.
   
   Exemple (pour référence):
   # setup.cfg (moins utilisé maintenant)
   [metadata]
   name = monmodule
   version = 1.0.0
   author = Votre Nom
   description = Description courte
   
   [options]
   packages = find:
   python_requires = >=3.8
   install_requires =
       requests>=2.28.0
   
   [options.packages.find]
   exclude = tests*
   
   [tool:pytest]
   testpaths = tests
   
   Quand l'utiliser:
   - Uniquement si vous maintenez un vieux projet
   - Si un outil spécifique le requiert (de plus en plus rare)
   - Préférez toujours pyproject.toml pour nouveaux projets


═══════════════════════════════════════════════════════════════════
FICHIERS ADDITIONNELS POSSIBLES
═══════════════════════════════════════════════════════════════════

[26] .coveragerc ou [tool.coverage] dans pyproject.toml
────────────────────────────────────────────────────────────────────
   - Configuration pour coverage.py (mesure couverture de tests)
   - Définit quels fichiers inclure/exclure, seuil minimum, format rapport
   
   Exemple dans pyproject.toml:
   [tool.coverage.run]
   source = ["monmodule"]
   omit = ["*/tests/*", "*/test_*.py"]
   
   [tool.coverage.report]
   exclude_lines = [
       "pragma: no cover",
       "def __repr__",
       "raise NotImplementedError",
   ]


[27] .flake8 ou setup.cfg [flake8]
────────────────────────────────────────────────────────────────────
   - Configuration pour flake8 (linter Python)
   - Malheureusement flake8 ne supporte PAS pyproject.toml
   - Doit être dans .flake8 ou setup.cfg
   
   Exemple .flake8:
   [flake8]
   max-line-length = 88
   extend-ignore = E203, W503
   exclude = .git,__pycache__,venv


[28] .python-version
────────────────────────────────────────────────────────────────────
   - Fichier pour pyenv (gestionnaire de versions Python)
   - Contient juste le numéro de version: 3.11.5
   - pyenv utilise automatiquement cette version dans ce dossier


[29] Makefile
────────────────────────────────────────────────────────────────────
   - Raccourcis pour commandes courantes
   - Facilite le développement
   
   Exemple:
   .PHONY: test lint format clean
   
   test:
       pytest
   
   lint:
       flake8 monmodule/
       mypy monmodule/
   
   format:
       black monmodule/ tests/
   
   clean:
       rm -rf build/ dist/ *.egg-info


[30] .dockerignore et Dockerfile
────────────────────────────────────────────────────────────────────
   - Pour containeriser votre application (si applicable)
   - .dockerignore: comme .gitignore mais pour Docker
   - Dockerfile: instructions pour construire l'image Docker


═══════════════════════════════════════════════════════════════════
RÉSUMÉ FINAL - ORDRE DE PRIORITÉ POUR DÉBUTANTS
═══════════════════════════════════════════════════════════════════

┌─────────────────────────────────────────────────────────────────┐
│ NIVEAU 1 - OBLIGATOIRES (ne pas publier sans)                  │
└─────────────────────────────────────────────────────────────────┘

1. monmodule/__init__.py     - Marque le package Python
2. monmodule/core.py         - Votre code fonctionnel
3. pyproject.toml            - Configuration du package
4. README.md                 - Documentation utilisateur
5. LICENSE                   - Droits légaux (MIT recommandée)

Temps estimé: 30 minutes
Action: Créez ces 5 fichiers en premier


┌─────────────────────────────────────────────────────────────────┐
│ NIVEAU 2 - FORTEMENT RECOMMANDÉS (projet sérieux)              │
└─────────────────────────────────────────────────────────────────┘

6. .gitignore                - Propreté du dépôt Git
7. tests/test_core.py        - Tests de base
8. CHANGELOG.md              - Historique des versions
9. MANIFEST.in               - Si vous avez des fichiers non-.py

Temps estimé: 1-2 heures
Action: Ajoutez-les avant la première version publique


┌─────────────────────────────────────────────────────────────────┐
│ NIVEAU 3 - QUALITÉ++ (projet professionnel)                    │
└─────────────────────────────────────────────────────────────────┘

10. requirements-dev.txt     - Facilite le développement
11. pytest.ini               - Configure les tests
12. .pre-commit-config.yaml  - Qualité automatique du code
13. conftest.py              - Fixtures pour tests

Temps estimé: 2-3 heures
Action: Ajoutez progressivement selon vos besoins


┌─────────────────────────────────────────────────────────────────┐
│ NIVEAU 4 - AVANCÉ (projet mature avec communauté)              │
└─────────────────────────────────────────────────────────────────┘

14. tox.ini                  - Tests multi-versions Python
15. py.typed                 - Support type hints
16. docs/                    - Documentation Sphinx
17. .github/workflows/       - CI/CD automatique
18. CONTRIBUTING.md          - Guide contributeurs
19. SECURITY.md              - Politique sécurité

Temps estimé: 1-2 jours
Action: Quand le projet a des utilisateurs


┌─────────────────────────────────────────────────────────────────┐
│ NIVEAU 5 - OPTIONNEL (selon contexte)                          │
└─────────────────────────────────────────────────────────────────┘

20. .editorconfig            - Si travail en équipe
21. mypy.ini                 - Si type hints stricts
22. Makefile                 - Pour raccourcis commandes
23. .python-version          - Si vous utilisez pyenv

Action: Selon vos besoins spécifiques


═══════════════════════════════════════════════════════════════════
CHECKLIST DE DÉMARRAGE RAPIDE
═══════════════════════════════════════════════════════════════════

Pour créer un nouveau module en 10 minutes:

[WHITE_SQUARE] mkdir monmodule && cd monmodule
[WHITE_SQUARE] git init
[WHITE_SQUARE] Créer monmodule/__init__.py (peut être vide)
[WHITE_SQUARE] Créer monmodule/core.py (votre code)
[WHITE_SQUARE] Créer pyproject.toml (copier template ci-dessous)
[WHITE_SQUARE] Créer README.md (titre + description + installation)
[WHITE_SQUARE] Copier LICENSE MIT depuis choosealicense.com
[WHITE_SQUARE] Créer .gitignore (copier template Python)
[WHITE_SQUARE] git add . && git commit -m "Initial commit"
[WHITE_SQUARE] Créer tests/test_core.py (un test simple)
[WHITE_SQUARE] pytest (vérifier que ça marche)

TEMPLATE pyproject.toml minimal:
───────────────────────────────────────────────────────────────────
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "monmodule"
version = "0.1.0"
description = "Description en une ligne"
authors = [{name = "Votre Nom", email = "email@example.com"}]
readme = "README.md"
requires-python = ">=3.8"
classifiers = [
    "Programming Language :: Python :: 3",
    "License :: OSI Approved :: MIT License",
]
dependencies = []

[project.urls]
Homepage = "https://github.com/username/monmodule"
───────────────────────────────────────────────────────────────────


═══════════════════════════════════════════════════════════════════
ERREURS COURANTES À ÉVITER
═══════════════════════════════════════════════════════════════════

[X] Oublier __init__.py -> Python ne reconnaît pas le package
[X] Pas de LICENSE -> Personne ne peut légalement utiliser votre code
[X] Pas de tests -> Bugs non détectés
[X] Pas de .gitignore -> Dépôt Git pollué
[X] Version codée en dur partout -> Difficile à maintenir
[X] Utiliser setup.py en 2025 -> Obsolète, utiliser pyproject.toml
[X] Pas de CHANGELOG -> Utilisateurs perdus lors des mises à jour
[X] Commit de __pycache__/ -> Ajouter à .gitignore
[X] Pas de README -> Personne ne sait utiliser votre module


═══════════════════════════════════════════════════════════════════
FIN DU GUIDE - TOUS LES FICHIERS POSSIBLES EXPLIQUÉS
═══════════════════════════════════════════════════════════════════

Vous connaissez maintenant TOUS les fichiers standards pour créer
un module Python professionnel, de la structure minimale (5 fichiers)
jusqu'au projet enterprise complet (30+ fichiers).

Commencez simple (Niveau 1-2), puis ajoutez progressivement selon
vos besoins et la maturité de votre projet.

Bonne création de modules! [PYTHON][PACKAGE]


═══════════════════════════════════════════════════════════════════
ÉTAPE 2: CRÉER VOTRE PREMIER MODULE - EXEMPLE COMPLET
═══════════════════════════════════════════════════════════════════

Nous allons créer "mathtools" - un module avec des fonctions mathématiques.

─────────────────────────────────────────────────────────────────
ÉTAPE 2.1: Créer la structure de dossiers
─────────────────────────────────────────────────────────────────

# Ouvrir un terminal et créer les dossiers
mkdir mathtools
cd mathtools
mkdir mathtools  # Oui, deux fois! Un pour le projet, un pour le code
mkdir tests

# Créer les fichiers vides
touch mathtools/__init__.py
touch mathtools/core.py
touch tests/__init__.py
touch tests/test_core.py
touch README.md
touch LICENSE
touch pyproject.toml

# Résultat:
mathtools/                    # Dossier racine du projet
├── mathtools/               # Package Python (code source)
│   ├── __init__.py
│   └── core.py
├── tests/                   # Tests
│   ├── __init__.py
│   └── test_core.py
├── README.md
├── LICENSE
└── pyproject.toml

─────────────────────────────────────────────────────────────────
ÉTAPE 2.2: Écrire le code de votre module
─────────────────────────────────────────────────────────────────

# FICHIER: mathtools/core.py
"""
Fonctions mathématiques utilitaires.

Ce module fournit des fonctions mathématiques supplémentaires.
"""

def add_numbers(a, b):
    """
    Additionne deux nombres.
    
    Args:
        a (int/float): Premier nombre
        b (int/float): Deuxième nombre
        
    Returns:
        int/float: La somme de a et b
        
    Examples:
        >>> add_numbers(2, 3)
        5
        >>> add_numbers(1.5, 2.5)
        4.0
    """
    return a + b


def multiply_numbers(a, b):
    """
    Multiplie deux nombres.
    
    Args:
        a (int/float): Premier nombre
        b (int/float): Deuxième nombre
        
    Returns:
        int/float: Le produit de a et b
    """
    return a * b


def factorial(n):
    """
    Calcule la factorielle d'un nombre.
    
    Args:
        n (int): Nombre positif
        
    Returns:
        int: La factorielle de n
        
    Raises:
        ValueError: Si n est négatif
        
    Examples:
        >>> factorial(5)
        120
        >>> factorial(0)
        1
    """
    if n < 0:
        raise ValueError("n doit être positif ou nul")
    if n == 0 or n == 1:
        return 1
    result = 1
    for i in range(2, n + 1):
        result *= i
    return result


# EXPLICATION DU CODE:
# - Les docstrings (""" ... """) documentent chaque fonction
# - Format standardisé pour que Sphinx puisse générer la doc
# - Examples dans les docstrings servent aussi de tests (doctest)

─────────────────────────────────────────────────────────────────
ÉTAPE 2.3: Configurer __init__.py
─────────────────────────────────────────────────────────────────

# FICHIER: mathtools/__init__.py
"""
MathTools - Utilitaires mathématiques pour Python.

Un module simple fournissant des fonctions mathématiques utiles.
"""

# Version du module (IMPORTANT: à changer à chaque mise à jour)
__version__ = "0.1.0"

# Informations sur l'auteur
__author__ = "Votre Nom"
__email__ = "votre.email@example.com"

# Importer les fonctions depuis core.py pour qu'elles soient accessibles
# directement depuis mathtools (import mathtools; mathtools.add_numbers())
from .core import add_numbers, multiply_numbers, factorial

# __all__ définit ce qui est exporté quand on fait "from mathtools import *"
# (optionnel mais bonne pratique)
__all__ = [
    "add_numbers",
    "multiply_numbers", 
    "factorial",
]

# EXPLICATION:
# Grâce à ces imports, les utilisateurs peuvent faire:
#     from mathtools import add_numbers
# Au lieu de:
#     from mathtools.core import add_numbers

─────────────────────────────────────────────────────────────────
ÉTAPE 2.4: Écrire des tests
─────────────────────────────────────────────────────────────────

# FICHIER: tests/test_core.py
"""
Tests pour le module mathtools.
"""
import pytest
from mathtools import add_numbers, multiply_numbers, factorial


def test_add_numbers():
    """Test de la fonction add_numbers."""
    assert add_numbers(2, 3) == 5
    assert add_numbers(0, 0) == 0
    assert add_numbers(-1, 1) == 0
    assert add_numbers(1.5, 2.5) == 4.0


def test_multiply_numbers():
    """Test de la fonction multiply_numbers."""
    assert multiply_numbers(2, 3) == 6
    assert multiply_numbers(0, 5) == 0
    assert multiply_numbers(-2, 3) == -6


def test_factorial():
    """Test de la fonction factorial."""
    assert factorial(0) == 1
    assert factorial(1) == 1
    assert factorial(5) == 120
    assert factorial(10) == 3628800


def test_factorial_negative():
    """Test que factorial lève une erreur pour nombres négatifs."""
    with pytest.raises(ValueError):
        factorial(-1)


# EXPLICATION DES TESTS:
# - pytest découvre automatiquement les fichiers test_*.py
# - Chaque fonction test_*() est un test
# - assert vérifie que la condition est vraie
# - pytest.raises() vérifie qu'une exception est levée

─────────────────────────────────────────────────────────────────
ÉTAPE 2.5: Créer pyproject.toml (CONFIGURATION PRINCIPALE)
─────────────────────────────────────────────────────────────────

# FICHIER: pyproject.toml
# Ceci est le fichier le plus important!

# ═══════════════════════════════════════════════════════════════
# Section 1: Système de build
# ═══════════════════════════════════════════════════════════════
[build-system]
# Outils nécessaires pour construire votre package
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

# EXPLICATION:
# - setuptools: outil standard pour créer des packages Python
# - wheel: format moderne de distribution (.whl)
# - Cette section est obligatoire

# ═══════════════════════════════════════════════════════════════
# Section 2: Métadonnées du projet
# ═══════════════════════════════════════════════════════════════
[project]
# Nom du module (DOIT être unique sur PyPI!)
# Vérifier disponibilité: https://pypi.org/project/mathtools/
name = "mathtools"

# Version (Semantic Versioning: MAJEUR.MINEUR.PATCH)
# 0.1.0 = première version alpha
# 0.2.0 = ajout de fonctionnalités
# 1.0.0 = première version stable
version = "0.1.0"

# Description courte (apparaît dans les recherches PyPI)
description = "Utilitaires mathématiques simples pour Python"

# README affiché sur la page PyPI
readme = "README.md"

# Version minimale de Python requise
requires-python = ">=3.8"

# Licence (MIT est la plus courante et permissive)
license = {text = "MIT"}

# Auteur(s)
authors = [
    {name = "Votre Nom", email = "votre.email@example.com"}
]

# Mots-clés pour la recherche sur PyPI
keywords = ["math", "mathematics", "utilities", "tools"]

# Classifiers (catégories sur PyPI)
# Liste complète: https://pypi.org/classifiers/
classifiers = [
    # Statut du développement
    "Development Status :: 3 - Alpha",
    
    # Public visé
    "Intended Audience :: Developers",
    "Intended Audience :: Education",
    
    # Type de projet
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Scientific/Engineering :: Mathematics",
    
    # Licence (doit correspondre à votre fichier LICENSE)
    "License :: OSI Approved :: MIT License",
    
    # Versions Python supportées
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.8",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    
    # Systèmes d'exploitation
    "Operating System :: OS Independent",
]

# Dépendances (modules nécessaires à votre module)
# Vide si pas de dépendances
dependencies = []

# Si vous aviez des dépendances:
# dependencies = [
#     "requests>=2.28.0",      # Version minimale
#     "numpy>=1.20,<2.0",      # Entre 1.20 et 2.0
# ]

# ═══════════════════════════════════════════════════════════════
# Section 3: Dépendances optionnelles
# ═══════════════════════════════════════════════════════════════
[project.optional-dependencies]
# Dépendances pour le développement (tests, linting)
dev = [
    "pytest>=7.0.0",
    "pytest-cov>=4.0.0",
    "black>=23.0.0",
    "flake8>=6.0.0",
]

# Les utilisateurs peuvent installer avec: pip install mathtools[dev]

# ═══════════════════════════════════════════════════════════════
# Section 4: URLs du projet
# ═══════════════════════════════════════════════════════════════
[project.urls]
"Homepage" = "https://github.com/votre-username/mathtools"
"Bug Reports" = "https://github.com/votre-username/mathtools/issues"
"Source" = "https://github.com/votre-username/mathtools"

# REMPLACER "votre-username" par votre nom d'utilisateur GitHub

# ═══════════════════════════════════════════════════════════════
# Section 5: Configuration setuptools
# ═══════════════════════════════════════════════════════════════
[tool.setuptools]
# Packages à inclure dans la distribution
# find: découvre automatiquement les packages
packages = ["mathtools"]

# ═══════════════════════════════════════════════════════════════
# Section 6: Configuration des outils de développement
# ═══════════════════════════════════════════════════════════════

# Configuration de pytest (framework de test)
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
addopts = [
    "--verbose",
    "--cov=mathtools",
    "--cov-report=term-missing",
]

# Configuration de black (formatage automatique du code)
[tool.black]
line-length = 88
target-version = ['py38']

─────────────────────────────────────────────────────────────────
ÉTAPE 2.6: Créer le README.md
─────────────────────────────────────────────────────────────────

# FICHIER: README.md
# Ce fichier s'affiche sur la page PyPI de votre module

```markdown
# MathTools

Utilitaires mathématiques simples pour Python.

## Installation

```bash
pip install mathtools
```

## Utilisation

```python
from mathtools import add_numbers, multiply_numbers, factorial

# Addition
result = add_numbers(5, 3)
print(result)  # 8

# Multiplication
result = multiply_numbers(4, 7)
print(result)  # 28

# Factorielle
result = factorial(5)
print(result)  # 120
```

## Fonctionnalités

- `add_numbers(a, b)`: Additionne deux nombres
- `multiply_numbers(a, b)`: Multiplie deux nombres
- `factorial(n)`: Calcule la factorielle de n

## Développement

Pour contribuer au projet:

```bash
# Cloner le repository
git clone https://github.com/votre-username/mathtools.git
cd mathtools

# Créer un environnement virtuel
python -m venv .venv
source .venv/bin/activate  # Linux/Mac
# ou
.venv\Scripts\activate  # Windows

# Installer en mode développement
pip install -e ".[dev]"

# Lancer les tests
pytest
```

## Licence

MIT License - voir le fichier LICENSE
```

─────────────────────────────────────────────────────────────────
ÉTAPE 2.7: Créer le fichier LICENSE
─────────────────────────────────────────────────────────────────

# FICHIER: LICENSE
# Licence MIT (la plus permissive et courante)

MIT License

Copyright (c) 2025 Votre Nom

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

# EXPLICATION DES LICENCES:
# - MIT: Très permissive, permet usage commercial, modification
# - Apache 2.0: Permissive avec protection des brevets
# - GPL: Copyleft, force partage des modifications
# Choisir MIT si vous êtes indécis


═══════════════════════════════════════════════════════════════════
ÉTAPE 3: TESTER VOTRE MODULE LOCALEMENT
═══════════════════════════════════════════════════════════════════

Avant de publier sur PyPI, il faut tester que tout fonctionne!

─────────────────────────────────────────────────────────────────
ÉTAPE 3.1: Créer un environnement virtuel
─────────────────────────────────────────────────────────────────

# Dans le dossier mathtools/ (racine)
python -m venv .venv

# Activer l'environnement
# Linux/Mac:
source .venv/bin/activate

# Windows (CMD):
.venv\Scripts\activate.bat

# Windows (PowerShell):
.venv\Scripts\Activate.ps1

# Votre prompt devrait maintenant afficher (.venv)

# POURQUOI un environnement virtuel?
# - Isole les dépendances de votre projet
# - Évite les conflits avec d'autres projets
# - Permet de tester l'installation proprement

─────────────────────────────────────────────────────────────────
ÉTAPE 3.2: Installer votre module en mode développement
─────────────────────────────────────────────────────────────────

# Installer votre module en mode "editable"
pip install -e .

# Le point "." signifie "le dossier courant"
# L'option -e (editable) permet de modifier le code sans réinstaller

# Installer avec les dépendances de développement
pip install -e ".[dev]"

# VÉRIFICATION:
# Si tout s'est bien passé, vous devriez voir:
# "Successfully installed mathtools-0.1.0"

─────────────────────────────────────────────────────────────────
ÉTAPE 3.3: Tester l'import de votre module
─────────────────────────────────────────────────────────────────

# Ouvrir Python dans le terminal
python

# Dans l'interpréteur Python:
>>> import mathtools
>>> print(mathtools.__version__)
0.1.0

>>> from mathtools import add_numbers, factorial
>>> add_numbers(5, 3)
8

>>> factorial(5)
120

>>> exit()

# Si tout fonctionne: SUCCÈS! Votre module est installable.

─────────────────────────────────────────────────────────────────
ÉTAPE 3.4: Lancer les tests
─────────────────────────────────────────────────────────────────

# Revenir au terminal (environnement virtuel activé)
pytest

# Vous devriez voir:
# ====== test session starts ======
# tests/test_core.py ....    [100%]
# ====== 4 passed in 0.05s ======

# Si un test échoue, corriger le code avant de continuer

# Test avec couverture de code (optionnel)
pytest --cov=mathtools

# Affiche quel pourcentage de votre code est testé


═══════════════════════════════════════════════════════════════════
ÉTAPE 4: PRÉPARER LA PUBLICATION SUR PYPI
═══════════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────────────────
ÉTAPE 4.1: Créer un compte PyPI
─────────────────────────────────────────────────────────────────

1. Aller sur https://test.pypi.org/account/register/
   - C'est le serveur de TEST (pour s'entraîner)
   - Créer un compte
   - Vérifier votre email

2. Aller sur https://pypi.org/account/register/
   - C'est le serveur PRODUCTION (réel)
   - Créer un compte
   - Vérifier votre email

IMPORTANT: Commencez TOUJOURS par TestPyPI!

─────────────────────────────────────────────────────────────────
ÉTAPE 4.2: Créer un API Token (recommandé)
─────────────────────────────────────────────────────────────────

Au lieu d'utiliser votre mot de passe, PyPI recommande les API tokens.

# Sur TestPyPI:
1. Connectez-vous sur https://test.pypi.org
2. Aller dans Account Settings
3. Cliquer sur "Add API token"
4. Nom: "mathtools-upload" (ou autre)
5. Scope: "Entire account" (pour le premier upload)
6. Copier le token (commence par pypi-...)
   ATTENTION: Il ne sera affiché qu'une seule fois!

# Faire pareil sur PyPI (production):
1. https://pypi.org -> Account Settings -> Add API token

─────────────────────────────────────────────────────────────────
ÉTAPE 4.3: Configurer les tokens
─────────────────────────────────────────────────────────────────

# Créer le fichier de configuration ~/.pypirc
# Linux/Mac:
nano ~/.pypirc

# Windows (créer dans C:\Users\VotreNom\.pypirc)
notepad %USERPROFILE%\.pypirc

# Contenu du fichier:
[distutils]
index-servers =
    pypi
    testpypi

[pypi]
username = __token__
password = pypi-VOTRE_TOKEN_PRODUCTION_ICI

[testpypi]
username = __token__
password = pypi-VOTRE_TOKEN_TEST_ICI

# REMPLACER les tokens par ceux copiés précédemment

# Sécuriser le fichier (Linux/Mac seulement)
chmod 600 ~/.pypirc

# ALTERNATIVE: Passer les tokens directement dans la commande (plus sûr)
# On verra ça plus tard


═══════════════════════════════════════════════════════════════════
ÉTAPE 5: CONSTRUIRE VOTRE PACKAGE
═══════════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────────────────
ÉTAPE 5.1: Installer les outils de build
─────────────────────────────────────────────────────────────────

# Dans votre environnement virtuel
pip install build twine

# build: construit le package (.tar.gz et .whl)
# twine: upload le package sur PyPI

─────────────────────────────────────────────────────────────────
ÉTAPE 5.2: Construire le package
─────────────────────────────────────────────────────────────────

# Dans le dossier mathtools/ (racine)
python -m build

# Vous verrez des messages comme:
# * Creating venv isolated environment...
# * Installing packages in isolated environment...
# * Building wheel...
# Successfully built mathtools-0.1.0.tar.gz and mathtools-0.1.0-py3-none-any.whl

# DEUX fichiers sont créés dans dist/:
# 1. mathtools-0.1.0.tar.gz (source distribution)
# 2. mathtools-0.1.0-py3-none-any.whl (wheel, distribution binaire)

─────────────────────────────────────────────────────────────────
ÉTAPE 5.3: Vérifier les packages créés
─────────────────────────────────────────────────────────────────

# Vérifier que les packages sont valides
twine check dist/*

# Vous devriez voir:
# Checking dist/mathtools-0.1.0.tar.gz: PASSED
# Checking dist/mathtools-0.1.0-py3-none-any.whl: PASSED

# Si "PASSED": tout est bon!
# Si erreur: corriger et reconstruire

# Voir le contenu du wheel (optionnel)
unzip -l dist/mathtools-0.1.0-py3-none-any.whl

# Ou le tar.gz
tar -tzf dist/mathtools-0.1.0.tar.gz


═══════════════════════════════════════════════════════════════════
ÉTAPE 6: PUBLIER SUR TESTPYPI (TEST D'ABORD!)
═══════════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────────────────
ÉTAPE 6.1: Upload sur TestPyPI
─────────────────────────────────────────────────────────────────

# Upload vers TestPyPI
twine upload --repository testpypi dist/*

# Vous verrez:
# Uploading distributions to https://test.pypi.org/legacy/
# Uploading mathtools-0.1.0-py3-none-any.whl
# Uploading mathtools-0.1.0.tar.gz
# View at: https://test.pypi.org/project/mathtools/0.1.0/

# Si vous n'avez pas configuré ~/.pypirc, vous serez invité à entrer:
# Username: __token__
# Password: pypi-VOTRE_TOKEN_TEST

─────────────────────────────────────────────────────────────────
ÉTAPE 6.2: Vérifier sur le site web
─────────────────────────────────────────────────────────────────

# Ouvrir le lien affiché dans le terminal
# Exemple: https://test.pypi.org/project/mathtools/0.1.0/

# Vérifier:
# [OK] Le nom du projet
# [OK] La version
# [OK] La description
# [OK] Le README s'affiche correctement
# [OK] Les classifiers sont corrects
# [OK] Les liens fonctionnent

─────────────────────────────────────────────────────────────────
ÉTAPE 6.3: Tester l'installation depuis TestPyPI
─────────────────────────────────────────────────────────────────

# Créer un NOUVEAU environnement virtuel pour tester
cd ..
mkdir test-install
cd test-install
python -m venv .venv
source .venv/bin/activate  # ou .venv\Scripts\activate sur Windows

# Installer depuis TestPyPI
pip install --index-url https://test.pypi.org/simple/ mathtools

# ATTENTION: Si votre module a des dépendances, ajouter:
pip install --index-url https://test.pypi.org/simple/ \
            --extra-index-url https://pypi.org/simple/ \
            mathtools

# Tester que ça fonctionne
python -c "from mathtools import add_numbers; print(add_numbers(5, 3))"

# Devrait afficher: 8

# Si tout fonctionne: SUCCÈS!
# Votre module est prêt pour PyPI (production)


═══════════════════════════════════════════════════════════════════
ÉTAPE 7: PUBLIER SUR PYPI (PRODUCTION)
═══════════════════════════════════════════════════════════════════

[ATTENTION]  ATTENTION: Une fois publié sur PyPI, vous ne pouvez PAS:
   - Supprimer une version publiée
   - Modifier les fichiers d'une version
   - Réutiliser un numéro de version

Assurez-vous que tout est parfait avant de continuer!

─────────────────────────────────────────────────────────────────
ÉTAPE 7.1: Vérifications finales
─────────────────────────────────────────────────────────────────

[x] Le module fonctionne sur TestPyPI
[x] Tous les tests passent
[x] Le README est correct et bien formaté
[x] La licence est présente
[x] Les informations dans pyproject.toml sont exactes
[x] Le nom du module est disponible sur PyPI (pas déjà pris)

# Vérifier disponibilité du nom:
# Aller sur https://pypi.org/project/mathtools/
# Si "404 Not Found": le nom est disponible
# Si le projet existe: choisir un autre nom

─────────────────────────────────────────────────────────────────
ÉTAPE 7.2: Upload sur PyPI
─────────────────────────────────────────────────────────────────

# Upload vers PyPI (production)
twine upload dist/*

# Vous verrez:
# Uploading distributions to https://upload.pypi.org/legacy/
# Uploading mathtools-0.1.0-py3-none-any.whl
# Uploading mathtools-0.1.0.tar.gz
# View at: https://pypi.org/project/mathtools/0.1.0/

# FÉLICITATIONS! Votre module est maintenant public!

─────────────────────────────────────────────────────────────────
ÉTAPE 7.3: Vérifier et installer
─────────────────────────────────────────────────────────────────

# Visiter la page de votre projet
https://pypi.org/project/mathtools/

# N'importe qui peut maintenant installer votre module:
pip install mathtools

# Tester dans un nouvel environnement
cd ..
mkdir test-pypi-install
cd test-pypi-install
python -m venv .venv
source .venv/bin/activate

pip install mathtools
python -c "from mathtools import factorial; print(factorial(5))"

# SUCCÈS! Votre module est publié et fonctionnel!


═══════════════════════════════════════════════════════════════════
ÉTAPE 8: METTRE À JOUR VOTRE MODULE
═══════════════════════════════════════════════════════════════════

Quand vous corrigez des bugs ou ajoutez des fonctionnalités:

─────────────────────────────────────────────────────────────────
ÉTAPE 8.1: Versionnement Sémantique (Semantic Versioning)
─────────────────────────────────────────────────────────────────

Format: MAJEUR.MINEUR.PATCH (exemple: 2.4.1)

MAJEUR (breaking changes):
- Changements incompatibles avec les versions précédentes
- Exemple: 1.0.0 -> 2.0.0
- Renommer une fonction, changer signature, supprimer fonctionnalité

MINEUR (nouvelles fonctionnalités):
- Ajout de nouvelles fonctionnalités compatibles
- Exemple: 1.2.0 -> 1.3.0
- Ajouter une nouvelle fonction sans toucher aux anciennes

PATCH (corrections de bugs):
- Corrections de bugs, pas de nouvelles fonctionnalités
- Exemple: 1.2.3 -> 1.2.4
- Corriger un bug dans une fonction existante

VERSIONS PRÉLIMINAIRES:
- Alpha: 0.1.0, 0.2.0 (développement initial, instable)
- Beta: 0.9.0, 0.9.1 (presque stable, tests)
- Release Candidate: 1.0.0rc1, 1.0.0rc2 (candidat pour release)
- Stable: 1.0.0 (première version stable pour production)

─────────────────────────────────────────────────────────────────
ÉTAPE 8.2: Processus de mise à jour
─────────────────────────────────────────────────────────────────

# 1. MODIFIER LE CODE
# Exemple: Ajouter une nouvelle fonction dans mathtools/core.py

def power(base, exponent):
    """
    Élève un nombre à une puissance.
    
    Args:
        base (int/float): Le nombre de base
        exponent (int/float): L'exposant
        
    Returns:
        float: Le résultat de base^exponent
        
    Examples:
        >>> power(2, 3)
        8
        >>> power(5, 2)
        25
    """
    return base ** exponent


# 2. METTRE À JOUR __init__.py
# Ajouter le nouvel import
from .core import add_numbers, multiply_numbers, factorial, power

__all__ = [
    "add_numbers",
    "multiply_numbers", 
    "factorial",
    "power",  # Nouveau!
]


# 3. CHANGER LA VERSION
# Dans pyproject.toml:
version = "0.2.0"  # Était 0.1.0 (nouvelle fonctionnalité = MINEUR)

# Dans mathtools/__init__.py:
__version__ = "0.2.0"


# 4. AJOUTER DES TESTS
# Dans tests/test_core.py:
def test_power():
    """Test de la fonction power."""
    assert power(2, 3) == 8
    assert power(5, 2) == 25
    assert power(10, 0) == 1
    assert power(2, -1) == 0.5


# 5. METTRE À JOUR LE README
# Ajouter la nouvelle fonction dans la documentation


# 6. CRÉER UN CHANGELOG
# Créer le fichier CHANGELOG.md à la racine du projet

─────────────────────────────────────────────────────────────────
ÉTAPE 8.3: Créer un CHANGELOG
─────────────────────────────────────────────────────────────────

# FICHIER: CHANGELOG.md (nouveau fichier)
# Documente tous les changements entre versions

```markdown
# Changelog

Tous les changements notables de ce projet seront documentés ici.

Le format est basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.0.0/),
et ce projet adhère au [Semantic Versioning](https://semver.org/lang/fr/).

## [Non publié]

Changements en cours de développement.

## [0.2.0] - 2025-01-15

### Ajouté
- Nouvelle fonction `power(base, exponent)` pour calculer les puissances
- Tests pour la fonction power
- Documentation améliorée dans le README

### Modifié
- Amélioration des docstrings pour plus de clarté

### Corrigé
- Aucun

## [0.1.0] - 2025-01-10

### Ajouté
- Première version publiée
- Fonctions: `add_numbers`, `multiply_numbers`, `factorial`
- Tests unitaires complets
- Documentation de base

[Non publié]: https://github.com/username/mathtools/compare/v0.2.0...HEAD
[0.2.0]: https://github.com/username/mathtools/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/username/mathtools/releases/tag/v0.1.0
```

─────────────────────────────────────────────────────────────────
ÉTAPE 8.4: Publier la mise à jour
─────────────────────────────────────────────────────────────────

# 1. Vérifier que tout fonctionne
pytest

# 2. Supprimer les anciens builds
rm -rf dist/ build/ *.egg-info
# Windows: rmdir /s dist build & del /s *.egg-info

# 3. Construire la nouvelle version
python -m build

# 4. Vérifier les packages
twine check dist/*

# 5. Tester sur TestPyPI d'abord (recommandé)
twine upload --repository testpypi dist/*

# 6. Tester l'installation
pip install --index-url https://test.pypi.org/simple/ mathtools==0.2.0

# 7. Si tout est OK, publier sur PyPI
twine upload dist/*

# 8. Créer un tag Git (optionnel mais recommandé)
git tag -a v0.2.0 -m "Version 0.2.0 - Ajout fonction power"
git push origin v0.2.0


═══════════════════════════════════════════════════════════════════
ÉTAPE 9: CONFIGURATION GIT ET GITHUB
═══════════════════════════════════════════════════════════════════

Git permet de versionner votre code et GitHub de le partager.

─────────────────────────────────────────────────────────────────
ÉTAPE 9.1: Créer .gitignore
─────────────────────────────────────────────────────────────────

# FICHIER: .gitignore (à la racine du projet)
# Liste les fichiers à ne PAS versionner

# Environnements virtuels Python
.venv/
venv/
env/
ENV/

# Fichiers compilés Python
__pycache__/
*.py[cod]
*$py.class
*.so

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

# Tests et couverture
.pytest_cache/
.coverage
htmlcov/
.tox/

# IDEs
.vscode/
.idea/
*.swp
*.swo
*~

# Système
.DS_Store
Thumbs.db

# Fichiers de configuration sensibles
.pypirc
.env

# Documentation générée
docs/_build/
docs/_static/
docs/_templates/

─────────────────────────────────────────────────────────────────
ÉTAPE 9.2: Initialiser Git
─────────────────────────────────────────────────────────────────

# Dans le dossier mathtools/ (racine)
git init

# Ajouter tous les fichiers
git add .

# Premier commit
git commit -m "Initial commit - MathTools v0.1.0"

# EXPLICATION:
# - git init: initialise un repository Git
# - git add .: ajoute tous les fichiers (sauf ceux dans .gitignore)
# - git commit: crée un snapshot de votre code

─────────────────────────────────────────────────────────────────
ÉTAPE 9.3: Créer un repository sur GitHub
─────────────────────────────────────────────────────────────────

# 1. Aller sur https://github.com
# 2. Cliquer sur "New repository" (ou +)
# 3. Remplir:
#    - Repository name: mathtools
#    - Description: Utilitaires mathématiques pour Python
#    - Public (pour que ce soit gratuit)
#    - NE PAS cocher "Initialize with README" (on l'a déjà)

# 4. Lier votre dépôt local à GitHub
git remote add origin https://github.com/votre-username/mathtools.git

# 5. Pousser le code vers GitHub
git branch -M main  # Renommer la branche en "main"
git push -u origin main

# Votre code est maintenant sur GitHub!
# URL: https://github.com/votre-username/mathtools

─────────────────────────────────────────────────────────────────
ÉTAPE 9.4: Créer un fichier CONTRIBUTING.md
─────────────────────────────────────────────────────────────────

# FICHIER: CONTRIBUTING.md
# Guide pour les contributeurs

```markdown
# Contribuer à MathTools

Merci de votre intérêt pour contribuer à MathTools!

## Comment contribuer

1. **Fork** le projet sur GitHub
2. **Cloner** votre fork:
   ```bash
   git clone https://github.com/votre-username/mathtools.git
   cd mathtools
   ```

3. **Créer une branche** pour votre fonctionnalité:
   ```bash
   git checkout -b feature/ma-nouvelle-fonction
   ```

4. **Installer** en mode développement:
   ```bash
   python -m venv .venv
   source .venv/bin/activate
   pip install -e ".[dev]"
   ```

5. **Développer** votre fonctionnalité:
   - Écrire du code propre et documenté
   - Ajouter des tests
   - Suivre le style de code existant

6. **Tester** votre code:
   ```bash
   pytest
   black mathtools/  # Formater le code
   flake8 mathtools/  # Vérifier le style
   ```

7. **Commiter** vos changements:
   ```bash
   git add .
   git commit -m "Ajout de la fonction xyz"
   ```

8. **Pousser** vers votre fork:
   ```bash
   git push origin feature/ma-nouvelle-fonction
   ```

9. **Créer une Pull Request** sur GitHub

## Standards de code

- Suivre PEP 8 (conventions Python)
- Ajouter des docstrings à toutes les fonctions
- Écrire des tests pour toute nouvelle fonctionnalité
- Maintenir une couverture de tests > 90%

## Signaler des bugs

Utilisez les [GitHub Issues](https://github.com/votre-username/mathtools/issues)
```


═══════════════════════════════════════════════════════════════════
ÉTAPE 10: DOCUMENTATION AVEC SPHINX ET READ THE DOCS
═══════════════════════════════════════════════════════════════════

Read the Docs héberge gratuitement la documentation de votre module.

─────────────────────────────────────────────────────────────────
ÉTAPE 10.1: Installer Sphinx
─────────────────────────────────────────────────────────────────

# Ajouter Sphinx aux dépendances de développement
# Dans pyproject.toml, section [project.optional-dependencies]:
[project.optional-dependencies]
dev = [
    "pytest>=7.0.0",
    "pytest-cov>=4.0.0",
    "black>=23.0.0",
    "flake8>=6.0.0",
    "sphinx>=7.0.0",                    # Nouveau
    "sphinx-rtd-theme>=2.0.0",          # Thème Read the Docs
    "sphinx-autodoc-typehints>=1.24.0", # Support des type hints
]

# Installer les nouvelles dépendances
pip install -e ".[dev]"

─────────────────────────────────────────────────────────────────
ÉTAPE 10.2: Initialiser Sphinx
─────────────────────────────────────────────────────────────────

# Créer un dossier docs/ à la racine du projet
mkdir docs
cd docs

# Lancer l'assistant de configuration Sphinx
sphinx-quickstart

# RÉPONSES AUX QUESTIONS:
# > Separate source and build directories (y/n) [n]: y
# > Project name: MathTools
# > Author name(s): Votre Nom
# > Project release []: 0.1.0
# > Project language [en]: fr  (ou en)

# STRUCTURE CRÉÉE:
docs/
├── source/           # Fichiers source de la documentation
│   ├── conf.py      # Configuration Sphinx
│   ├── index.rst    # Page d'accueil
│   └── _static/     # Fichiers statiques (CSS, images)
├── build/           # Documentation générée (HTML)
└── Makefile         # Pour construire la doc (Linux/Mac)
└── make.bat         # Pour construire la doc (Windows)

─────────────────────────────────────────────────────────────────
ÉTAPE 10.3: Configurer Sphinx (conf.py)
─────────────────────────────────────────────────────────────────

# FICHIER: docs/source/conf.py
# Configuration complète de Sphinx

# ═══════════════════════════════════════════════════════════════
# Imports et configuration du path
# ═══════════════════════════════════════════════════════════════
import os
import sys

# Ajouter le chemin du module au PYTHONPATH
# Permet à Sphinx de trouver votre module
sys.path.insert(0, os.path.abspath('../../'))

# ═══════════════════════════════════════════════════════════════
# Informations du projet
# ═══════════════════════════════════════════════════════════════
project = 'MathTools'
copyright = '2025, Votre Nom'
author = 'Votre Nom'

# Version courte et complète
release = '0.1.0'
version = '0.1'

# ═══════════════════════════════════════════════════════════════
# Configuration générale
# ═══════════════════════════════════════════════════════════════

# Extensions Sphinx à utiliser
extensions = [
    'sphinx.ext.autodoc',           # Génère doc depuis docstrings
    'sphinx.ext.napoleon',          # Support Google/NumPy docstrings
    'sphinx.ext.viewcode',          # Ajoute liens vers code source
    'sphinx.ext.intersphinx',       # Liens vers autres docs
    'sphinx.ext.todo',              # Support des TODOs
    'sphinx.ext.coverage',          # Rapport de couverture doc
    'sphinx_autodoc_typehints',     # Support type hints
]

# Configuration napoleon (style de docstrings)
napoleon_google_docstring = True
napoleon_numpy_docstring = True
napoleon_include_init_with_doc = True

# Configuration autodoc
autodoc_default_options = {
    'members': True,           # Documenter tous les membres
    'undoc-members': True,     # Inclure membres non documentés
    'private-members': False,  # Ne pas documenter membres privés
    'special-members': '__init__',  # Documenter __init__
    'inherited-members': True,  # Documenter méthodes héritées
    'show-inheritance': True,   # Montrer l'héritage
}

# Fichiers à ignorer
templates_path = ['_templates']
exclude_patterns = []

# ═══════════════════════════════════════════════════════════════
# Configuration HTML (apparence)
# ═══════════════════════════════════════════════════════════════

# Thème à utiliser (Read the Docs theme)
html_theme = 'sphinx_rtd_theme'

# Options du thème
html_theme_options = {
    'navigation_depth': 4,
    'collapse_navigation': False,
    'sticky_navigation': True,
    'includehidden': True,
    'titles_only': False,
    'display_version': True,
}

# Fichiers statiques personnalisés (CSS, JS)
html_static_path = ['_static']

# Logo (optionnel)
# html_logo = '_static/logo.png'

# Favicon (optionnel)
# html_favicon = '_static/favicon.ico'

# ═══════════════════════════════════════════════════════════════
# Configuration intersphinx (liens vers autres docs)
# ═══════════════════════════════════════════════════════════════
intersphinx_mapping = {
    'python': ('https://docs.python.org/3', None),
    'numpy': ('https://numpy.org/doc/stable/', None),
}

─────────────────────────────────────────────────────────────────
ÉTAPE 10.4: Créer la structure de la documentation
─────────────────────────────────────────────────────────────────

# FICHIER: docs/source/index.rst
# Page d'accueil de la documentation (format reStructuredText)

.. MathTools documentation master file

Bienvenue dans la documentation de MathTools!
=============================================

MathTools est une bibliothèque Python fournissant des utilitaires
mathématiques simples et efficaces.

Installation
------------

Installez MathTools avec pip::

    pip install mathtools

Démarrage rapide
----------------

Exemple d'utilisation basique:

.. code-block:: python

    from mathtools import add_numbers, factorial
    
    # Addition
    result = add_numbers(5, 3)
    print(result)  # 8
    
    # Factorielle
    result = factorial(5)
    print(result)  # 120

Table des matières
------------------

.. toctree::
   :maxdepth: 2
   :caption: Contenu:

   installation
   quickstart
   api
   examples
   contributing
   changelog

Indices et tables
=================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`


# ───────────────────────────────────────────────────────────────
# FICHIER: docs/source/installation.rst
# Page d'installation détaillée

Installation
============

Installation stable
-------------------

La manière recommandée d'installer MathTools est via pip::

    pip install mathtools

Installation depuis les sources
--------------------------------

Pour installer la dernière version de développement::

    git clone https://github.com/votre-username/mathtools.git
    cd mathtools
    pip install -e ".[dev]"

Prérequis
---------

MathTools nécessite:

* Python >= 3.8
* Aucune dépendance externe

Vérification de l'installation
-------------------------------

Pour vérifier que MathTools est correctement installé::

    python -c "import mathtools; print(mathtools.__version__)"


# ───────────────────────────────────────────────────────────────
# FICHIER: docs/source/quickstart.rst
# Guide de démarrage rapide

Démarrage rapide
================

Ce guide vous aidera à commencer avec MathTools en quelques minutes.

Importation
-----------

Importez les fonctions dont vous avez besoin:

.. code-block:: python

    from mathtools import add_numbers, multiply_numbers, factorial

Exemples basiques
-----------------

Addition
~~~~~~~~

.. code-block:: python

    result = add_numbers(10, 20)
    print(result)  # 30

Multiplication
~~~~~~~~~~~~~~

.. code-block:: python

    result = multiply_numbers(5, 7)
    print(result)  # 35

Factorielle
~~~~~~~~~~~

.. code-block:: python

    result = factorial(6)
    print(result)  # 720

Gestion des erreurs
-------------------

MathTools lève des exceptions appropriées:

.. code-block:: python

    try:
        result = factorial(-5)
    except ValueError as e:
        print(f"Erreur: {e}")
    # Erreur: n doit être positif ou nul


# ───────────────────────────────────────────────────────────────
# FICHIER: docs/source/api.rst
# Référence API complète (générée automatiquement)

Référence API
=============

Cette page documente toutes les fonctions et classes de MathTools.

Module principal
----------------

.. automodule:: mathtools
   :members:
   :undoc-members:
   :show-inheritance:

Module core
-----------

.. automodule:: mathtools.core
   :members:
   :undoc-members:
   :show-inheritance:
   :private-members:


# ───────────────────────────────────────────────────────────────
# FICHIER: docs/source/examples.rst
# Page avec des exemples avancés

Exemples
========

Cette page contient des exemples d'utilisation avancée de MathTools.

Exemple 1: Calculs en chaîne
-----------------------------

.. code-block:: python

    from mathtools import add_numbers, multiply_numbers
    
    # Calculer: (5 + 3) * 2
    step1 = add_numbers(5, 3)
    result = multiply_numbers(step1, 2)
    print(result)  # 16

Exemple 2: Traitement de listes
--------------------------------

.. code-block:: python

    from mathtools import factorial
    
    # Calculer les factorielles de 1 à 5
    numbers = range(1, 6)
    factorials = [factorial(n) for n in numbers]
    print(factorials)  # [1, 2, 6, 24, 120]

Exemple 3: Validation des entrées
----------------------------------

.. code-block:: python

    from mathtools import factorial
    
    def safe_factorial(n):
        """Calcule la factorielle avec gestion d'erreurs."""
        try:
            return factorial(n)
        except ValueError:
            return None
    
    print(safe_factorial(5))   # 120
    print(safe_factorial(-1))  # None


# ───────────────────────────────────────────────────────────────
# FICHIER: docs/source/contributing.rst
# Guide de contribution (lien vers CONTRIBUTING.md)

Contribuer
==========

Nous accueillons les contributions de la communauté!

.. include:: ../../CONTRIBUTING.md
   :parser: myst_parser.sphinx_


# ───────────────────────────────────────────────────────────────
# FICHIER: docs/source/changelog.rst
# Historique des versions

Historique des versions
========================

.. include:: ../../CHANGELOG.md
   :parser: myst_parser.sphinx_

─────────────────────────────────────────────────────────────────
ÉTAPE 10.5: Construire la documentation localement
─────────────────────────────────────────────────────────────────

# Dans le dossier docs/
cd docs

# Linux/Mac:
make html

# Windows:
make.bat html

# La documentation est générée dans docs/build/html/

# Ouvrir dans le navigateur:
# Linux/Mac:
open build/html/index.html

# Windows:
start build/html/index.html

# OU simplement:
python -m http.server 8000 -d build/html
# Puis ouvrir http://localhost:8000

# VÉRIFICATIONS:
# [OK] La page d'accueil s'affiche correctement
# [OK] Le menu de navigation fonctionne
# [OK] La page API contient vos fonctions
# [OK] Les docstrings sont bien formatées
# [OK] Les liens internes fonctionnent

─────────────────────────────────────────────────────────────────
ÉTAPE 10.6: Ajouter un fichier .readthedocs.yaml
─────────────────────────────────────────────────────────────────

# FICHIER: .readthedocs.yaml (à la racine du projet)
# Configuration pour Read the Docs

# Version du format de configuration
version: 2

# Configuration du build
build:
  os: ubuntu-22.04
  tools:
    python: "3.11"
  
  # Commandes à exécuter avant le build
  jobs:
    pre_build:
      - pip install -e ".[dev]"

# Configuration Sphinx
sphinx:
  configuration: docs/source/conf.py
  fail_on_warning: false

# Formats de sortie
formats:
  - pdf
  - epub

# Configuration Python
python:
  install:
    - method: pip
      path: .
      extra_requirements:
        - dev

─────────────────────────────────────────────────────────────────
ÉTAPE 10.7: Créer un compte Read the Docs
─────────────────────────────────────────────────────────────────

# 1. Aller sur https://readthedocs.org
# 2. Cliquer sur "Sign Up"
# 3. S'inscrire avec GitHub (recommandé)
#    - Cela permettra l'import automatique de vos projets

# AVANTAGES de l'inscription via GitHub:
# - Import automatique de tous vos repositories
# - Synchronisation automatique des mises à jour
# - Webhooks configurés automatiquement

─────────────────────────────────────────────────────────────────
ÉTAPE 10.8: Importer votre projet sur Read the Docs
─────────────────────────────────────────────────────────────────

# 1. Une fois connecté, aller dans "My Projects"
# 2. Cliquer sur "Import a Project"
# 3. Vous verrez la liste de vos repositories GitHub
# 4. Trouver "mathtools" et cliquer sur le bouton "+" à côté
# 5. Ou cliquer sur "Import Manually":
#    - Name: mathtools
#    - Repository URL: https://github.com/votre-username/mathtools
#    - Repository type: Git
#    - Default branch: main

# 6. Cliquer sur "Next"

# 7. Configuration du projet:
#    - Project name: MathTools (affiché sur le site)
#    - Language: French (ou English)
#    - Programming Language: Python
#    - Project homepage: https://github.com/votre-username/mathtools
#    - Tags: math, python, utilities

# 8. Cliquer sur "Finish"

# Read the Docs va maintenant:
# - Cloner votre repository
# - Détecter .readthedocs.yaml
# - Installer les dépendances
# - Construire la documentation avec Sphinx
# - Publier sur https://mathtools.readthedocs.io

─────────────────────────────────────────────────────────────────
ÉTAPE 10.9: Configurer les webhooks (automatique avec GitHub)
─────────────────────────────────────────────────────────────────

Si vous avez importé via GitHub, les webhooks sont déjà configurés!

# VÉRIFICATION:
# 1. Sur GitHub: Settings -> Webhooks
# 2. Vous devriez voir un webhook vers readthedocs.org
# 3. Chaque push vers GitHub déclenchera un build automatique

# SI LES WEBHOOKS NE SONT PAS CONFIGURÉS:
# 1. Sur Read the Docs: Admin -> Integrations
# 2. Copier l'URL du webhook
# 3. Sur GitHub: Settings -> Webhooks -> Add webhook
#    - Payload URL: [URL copiée]
#    - Content type: application/json
#    - Events: Just the push event
#    - Active: [OK]

─────────────────────────────────────────────────────────────────
ÉTAPE 10.10: Personnaliser Read the Docs
─────────────────────────────────────────────────────────────────

# Sur Read the Docs, aller dans Admin de votre projet

# ONGLET: Advanced Settings
# ─────────────────────────
# Default version: latest (ou stable)
# Default branch: main
# Privacy Level: Public (pour open source)
# Analytics code: (optionnel, Google Analytics)

# Show version warning: [OK] (avertit si on lit une vieille version)
# Build pull requests: [OK] (teste les PRs avant merge)

# ONGLET: Versions
# ─────────────────────────
# Liste toutes les versions/tags de votre projet
# Activer les versions que vous voulez publier:
# - latest: dernière version de la branche main
# - stable: dernière release taggée
# - v0.1.0, v0.2.0: versions spécifiques

# Pour activer une version:
# 1. Cocher "Active"
# 2. Cocher "Public" 
# 3. Cliquer "Save"

# ONGLET: Automation Rules
# ─────────────────────────
# Règles pour publier automatiquement:
# - Activer automatiquement les nouvelles branches
# - Publier les tags matchant un pattern (ex: v*)

# Exemple de règle:
# Match: v*  (tous les tags commençant par v)
# Version type: Tag
# Action: Activate version
# Priority: 0

─────────────────────────────────────────────────────────────────
ÉTAPE 10.11: Badge Read the Docs pour votre README
─────────────────────────────────────────────────────────────────

# Ajouter un badge dans README.md pour montrer le statut de la doc

# FICHIER: README.md (au début)
```markdown
# MathTools

[![Documentation Status](https://readthedocs.org/projects/mathtools/badge/?version=latest)](https://mathtools.readthedocs.io/fr/latest/?badge=latest)
[![PyPI version](https://badge.fury.io/py/mathtools.svg)](https://badge.fury.io/py/mathtools)
[![Python versions](https://img.shields.io/pypi/pyversions/mathtools.svg)](https://pypi.org/project/mathtools/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Utilitaires mathématiques simples pour Python.

[Documentation](https://mathtools.readthedocs.io) | 
[PyPI](https://pypi.org/project/mathtools/) | 
[GitHub](https://github.com/votre-username/mathtools)

## Installation
...
```

# EXPLICATION DES BADGES:
# - Documentation Status: vert si build OK, rouge si erreur
# - PyPI version: version actuelle sur PyPI
# - Python versions: versions Python supportées
# - License: type de licence


═══════════════════════════════════════════════════════════════════
ÉTAPE 11: FONCTIONNALITÉS AVANCÉES DU MODULE
═══════════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────────────────
ÉTAPE 11.1: Ajouter des dépendances
─────────────────────────────────────────────────────────────────

Si votre module a besoin d'autres packages:

# FICHIER: pyproject.toml
[project]
dependencies = [
    "requests>=2.28.0",        # Version minimale
    "numpy>=1.20,<2.0",        # Plage de versions
    "pandas>=1.5.0",           # Dernière version compatible
]

# SYNTAXE DES VERSIONS:
# ==  : Version exacte         numpy==1.20.0
# >=  : Version minimale        numpy>=1.20.0
# <= : Version maximale        numpy<=2.0.0
# ~= : Compatible (patch)      numpy~=1.20.0  (1.20.x)
# != : Exclure une version     numpy!=1.21.0
# ,  : Combiner conditions     numpy>=1.20,<2.0

# DÉPENDANCES OPTIONNELLES:
[project.optional-dependencies]
# Pour les fonctionnalités extra
plotting = [
    "matplotlib>=3.5.0",
    "seaborn>=0.12.0",
]

# Pour le développement
dev = [
    "pytest>=7.0.0",
    "black>=23.0.0",
]

# Installation avec extras:
# pip install mathtools[plotting]
# pip install mathtools[dev]
# pip install mathtools[plotting,dev]

─────────────────────────────────────────────────────────────────
ÉTAPE 11.2: Créer des sous-modules
─────────────────────────────────────────────────────────────────

Pour organiser un gros projet:

# STRUCTURE AVEC SOUS-MODULES:
mathtools/
├── __init__.py
├── arithmetic/          # Sous-module arithmétique
│   ├── __init__.py
│   ├── basic.py        # Opérations de base
│   └── advanced.py     # Opérations avancées
├── geometry/           # Sous-module géométrie
│   ├── __init__.py
│   ├── shapes.py
│   └── vectors.py
└── utils/              # Utilitaires
    ├── __init__.py
    └── helpers.py

# ───────────────────────────────────────────────────────────────
# FICHIER: mathtools/arithmetic/__init__.py
"""Sous-module pour les opérations arithmétiques."""

from .basic import add, subtract, multiply, divide
from .advanced import power, sqrt, factorial

__all__ = [
    'add', 'subtract', 'multiply', 'divide',
    'power', 'sqrt', 'factorial'
]

# ───────────────────────────────────────────────────────────────
# FICHIER: mathtools/arithmetic/basic.py
"""Opérations arithmétiques de base."""

def add(a, b):
    """Additionne deux nombres."""
    return a + b

def subtract(a, b):
    """Soustrait b de a."""
    return a - b

# etc...

# ───────────────────────────────────────────────────────────────
# FICHIER: mathtools/__init__.py (mise à jour)
"""MathTools - Utilitaires mathématiques."""

__version__ = "1.0.0"

# Importer les sous-modules
from . import arithmetic
from . import geometry
from . import utils

# Importer les fonctions courantes pour accès direct
from .arithmetic import add, multiply, factorial
from .geometry import Circle, Vector

__all__ = [
    'arithmetic',
    'geometry',
    'utils',
    'add',
    'multiply',
    'factorial',
    'Circle',
    'Vector',
]

# UTILISATION:
# from mathtools import add                    # Import direct
# from mathtools.arithmetic import subtract    # Import depuis sous-module
# import mathtools.geometry as geom            # Import du sous-module

─────────────────────────────────────────────────────────────────
ÉTAPE 11.3: Ajouter des Entry Points (CLI)
─────────────────────────────────────────────────────────────────

Créer une commande en ligne de commande pour votre module.

# FICHIER: mathtools/cli.py (nouveau)
"""Interface en ligne de commande pour MathTools."""

import argparse
import sys
from . import factorial, add_numbers

def main():
    """Point d'entrée principal du CLI."""
    parser = argparse.ArgumentParser(
        description='MathTools - Utilitaires mathématiques'
    )
    
    subparsers = parser.add_subparsers(dest='command', help='Commandes')
    
    # Commande: factorial
    factorial_parser = subparsers.add_parser(
        'factorial', 
        help='Calcule la factorielle'
    )
    factorial_parser.add_argument(
        'number', 
        type=int, 
        help='Nombre pour calculer la factorielle'
    )
    
    # Commande: add
    add_parser = subparsers.add_parser('add', help='Additionne deux nombres')
    add_parser.add_argument('a', type=float, help='Premier nombre')
    add_parser.add_argument('b', type=float, help='Deuxième nombre')
    
    args = parser.parse_args()
    
    if args.command == 'factorial':
        try:
            result = factorial(args.number)
            print(f"factorial({args.number}) = {result}")
        except ValueError as e:
            print(f"Erreur: {e}", file=sys.stderr)
            sys.exit(1)
    
    elif args.command == 'add':
        result = add_numbers(args.a, args.b)
        print(f"{args.a} + {args.b} = {result}")
    
    else:
        parser.print_help()
        sys.exit(1)

if __name__ == '__main__':
    main()


# FICHIER: pyproject.toml (ajouter)
[project.scripts]
# Crée une commande "mathtools" qui appelle la fonction main()
mathtools = "mathtools.cli:main"

# UTILISATION après installation:
# $ mathtools factorial 5
# factorial(5) = 120

# $ mathtools add 10 20
# 10.0 + 20.0 = 30.0

─────────────────────────────────────────────────────────────────
ÉTAPE 11.4: Utiliser les ressources (data files)
─────────────────────────────────────────────────────────────────

Inclure des fichiers de données dans votre module.

# STRUCTURE:
mathtools/
├── __init__.py
├── core.py
└── data/
    ├── constants.json    # Constantes mathématiques
    └── formulas.yaml     # Formules

# ───────────────────────────────────────────────────────────────
# FICHIER: mathtools/data/constants.json
{
  "pi": 3.141592653589793,
  "e": 2.718281828459045,
  "phi": 1.618033988749895
}

# ───────────────────────────────────────────────────────────────
# FICHIER: mathtools/core.py (mise à jour)
"""Fonctions mathématiques avec accès aux ressources."""

import json
from importlib import resources

def get_constant(name):
    """
    Récupère une constante mathématique.
    
    Args:
        name (str): Nom de la constante (pi, e, phi)
        
    Returns:
        float: Valeur de la constante
        
    Raises:
        KeyError: Si la constante n'existe pas
    """
    # Python 3.9+
    try:
        with resources.files('mathtools').joinpath('data/constants.json').open() as f:
            constants = json.load(f)
    except AttributeError:
        # Python 3.7-3.8 (ancienne API)
        with resources.open_text('mathtools.data', 'constants.json') as f:
            constants = json.load(f)
    
    return constants[name]

# UTILISATION:
# >>> from mathtools import get_constant
# >>> get_constant('pi')
# 3.141592653589793

# ───────────────────────────────────────────────────────────────
# FICHIER: pyproject.toml (inclure les fichiers de données)
[tool.setuptools.package-data]
mathtools = ["data/*.json", "data/*.yaml"]

─────────────────────────────────────────────────────────────────
ÉTAPE 11.5: Gestion de la configuration
─────────────────────────────────────────────────────────────────

Permettre aux utilisateurs de configurer votre module.

# FICHIER: mathtools/config.py (nouveau)
"""Gestion de la configuration de MathTools."""

import os
from pathlib import Path
from typing import Any, Dict
import json

class Config:
    """Gestionnaire de configuration pour MathTools."""
    
    def __init__(self):
        self._config = self._default_config()
        self._load_config()
    
    def _default_config(self) -> Dict[str, Any]:
        """Configuration par défaut."""
        return {
            'precision': 10,           # Précision des calculs
            'use_cache': True,         # Utiliser le cache
            'verbose': False,          # Mode verbeux
            'output_format': 'decimal' # Format de sortie
        }
    
    def _config_path(self) -> Path:
        """Chemin du fichier de configuration utilisateur."""
        # Linux/Mac: ~/.config/mathtools/config.json
        # Windows: %APPDATA%\mathtools\config.json
        if os.name == 'nt':  # Windows
            base = Path(os.environ.get('APPDATA', '~'))
        else:  # Linux/Mac
            base = Path.home() / '.config'
        
        return base / 'mathtools' / 'config.json'
    
    def _load_config(self):
        """Charge la configuration depuis le fichier."""
        config_file = self._config_path()
        if config_file.exists():
            with open(config_file, 'r') as f:
                user_config = json.load(f)
                self._config.update(user_config)
    
    def save_config(self):
        """Sauvegarde la configuration dans le fichier."""
        config_file = self._config_path()
        config_file.parent.mkdir(parents=True, exist_ok=True)
        with open(config_file, 'w') as f:
            json.dump(self._config, f, indent=2)
    
    def get(self, key: str, default: Any = None) -> Any:
        """Récupère une valeur de configuration."""
        return self._config.get(key, default)
    
    def set(self, key: str, value: Any):
        """Définit une valeur de configuration."""
        self._config[key] = value
    
    def reset(self):
        """Réinitialise la configuration par défaut."""
        self._config = self._default_config()
        self.save_config()

# Instance globale
_config = Config()

def get_config() -> Config:
    """Récupère l'instance de configuration."""
    return _config

# ───────────────────────────────────────────────────────────────
# FICHIER: mathtools/__init__.py (mise à jour)
from .config import get_config

# UTILISATION:
# >>> from mathtools import get_config
# >>> config = get_config()
# >>> config.set('precision', 15)
# >>> config.save_config()
# >>> print(config.get('precision'))
# 15

─────────────────────────────────────────────────────────────────
ÉTAPE 11.6: Logging et débogage
─────────────────────────────────────────────────────────────────

Ajouter un système de logging professionnel.

# FICHIER: mathtools/logger.py (nouveau)
"""Système de logging pour MathTools."""

import logging
from pathlib import Path

def setup_logger(name: str = 'mathtools', 
                 level: int = logging.INFO,
                 log_file: str = None) -> logging.Logger:
    """
    Configure le logger pour MathTools.
    
    Args:
        name: Nom du logger
        level: Niveau de log (DEBUG, INFO, WARNING, ERROR, CRITICAL)
        log_file: Fichier de log (optionnel)
        
    Returns:
        Logger configuré
    """
    logger = logging.getLogger(name)
    logger.setLevel(level)
    
    # Format des messages
    formatter = logging.Formatter(
        '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S'
    )
    
    # Handler console
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)
    
    # Handler fichier (optionnel)
    if log_file:
        file_handler = logging.FileHandler(log_file)
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)
    
    return logger

# Logger global
logger = setup_logger()

# ───────────────────────────────────────────────────────────────
# FICHIER: mathtools/core.py (mise à jour avec logging)
from .logger import logger

def factorial(n):
    """Calcule la factorielle avec logging."""
    logger.debug(f"Calcul de factorial({n})")
    
    if n < 0:
        logger.error(f"Tentative de calcul de factorial avec n={n} (négatif)")
        raise ValueError("n doit être positif ou nul")
    
    if n == 0 or n == 1:
        logger.debug(f"factorial({n}) = 1 (cas de base)")
        return 1
    
    result = 1
    for i in range(2, n + 1):
        result *= i
    
    logger.info(f"factorial({n}) = {result}")
    return result

# UTILISATION:
# >>> import logging
# >>> from mathtools import factorial
# >>> from mathtools.logger import logger
# >>> logger.setLevel(logging.DEBUG)
# >>> factorial(5)
# 2025-01-15 10:30:45 - mathtools - DEBUG - Calcul de factorial(5)
# 2025-01-15 10:30:45 - mathtools - INFO - factorial(5) = 120
# 120

─────────────────────────────────────────────────────────────────
ÉTAPE 11.7: Exceptions personnalisées
─────────────────────────────────────────────────────────────────

Créer des exceptions spécifiques à votre module.

# FICHIER: mathtools/exceptions.py (nouveau)
"""Exceptions personnalisées pour MathTools."""

class MathToolsError(Exception):
    """Exception de base pour toutes les erreurs MathTools."""
    pass

class InvalidInputError(MathToolsError):
    """Levée quand l'entrée est invalide."""
    pass

class CalculationError(MathToolsError):
    """Levée quand un calcul échoue."""
    pass

class ConfigurationError(MathToolsError):
    """Levée quand la configuration est invalide."""
    pass

class PrecisionError(MathToolsError):
    """Levée quand la précision demandée n'est pas atteignable."""
    pass

# ───────────────────────────────────────────────────────────────
# FICHIER: mathtools/__init__.py (exporter les exceptions)
from .exceptions import (
    MathToolsError,
    InvalidInputError,
    CalculationError,
    ConfigurationError,
    PrecisionError,
)

# ───────────────────────────────────────────────────────────────
# FICHIER: mathtools/core.py (utiliser les exceptions)
from .exceptions import InvalidInputError

def factorial(n):
    """Calcule la factorielle avec exception personnalisée."""
    if not isinstance(n, int):
        raise InvalidInputError(
            f"n doit être un entier, pas {type(n).__name__}"
        )
    
    if n < 0:
        raise InvalidInputError(
            f"n doit être positif ou nul, pas {n}"
        )
    
    # ... reste du code

# UTILISATION:
# >>> from mathtools import factorial, InvalidInputError
# >>> try:
# ...     factorial(-5)
# ... except InvalidInputError as e:
# ...     print(f"Erreur: {e}")
# Erreur: n doit être positif ou nul, pas -5


═══════════════════════════════════════════════════════════════════
ÉTAPE 12: TESTS AVANCÉS ET CI/CD
═══════════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────────────────
ÉTAPE 12.1: Tests paramétrés avec pytest
─────────────────────────────────────────────────────────────────

# FICHIER: tests/test_core.py (avancé)
"""Tests avancés avec pytest."""

import pytest
from mathtools import factorial, add_numbers
from mathtools.exceptions import InvalidInputError

# Tests paramétrés (multiple cas de test en un)
@pytest.mark.parametrize("n,expected", [
    (0, 1),
    (1, 1),
    (5, 120),
    (10, 3628800),
    (15, 1307674368000),
])
def test_factorial_parametrized(n, expected):
    """Test factorial avec différentes valeurs."""
    assert factorial(n) == expected


# Tests avec fixtures (données réutilisables)
@pytest.fixture
def numbers():
    """Fixture fournissant des nombres de test."""
    return [1, 2, 3, 4, 5]

def test_with_fixture(numbers):
    """Test utilisant une fixture."""
    result = sum(add_numbers(a, b) for a, b in zip(numbers, numbers))
    assert result == 30  # 2+4+6+8+10


# Tests de performance
@pytest.mark.timeout(1)  # Doit finir en moins de 1 seconde
def test_factorial_performance():
    """Test que factorial est assez rapide."""
    for i in range(100):
        factorial(10)


# Tests conditionnels
@pytest.mark.skipif(
    sys.version_info < (3, 9),
    reason="Nécessite Python 3.9+"
)
def test_python39_feature():
    """Test pour fonctionnalité Python 3.9+."""
    pass


# Groupes de tests
@pytest.mark.slow
def test_heavy_computation():
    """Test marqué comme lent."""
    factorial(1000)

# Lancer uniquement les tests rapides:
# pytest -m "not slow"

─────────────────────────────────────────────────────────────────
ÉTAPE 12.2: Configuration GitHub Actions (CI/CD)
─────────────────────────────────────────────────────────────────

GitHub Actions permet de tester automatiquement votre code.

# FICHIER: .github/workflows/tests.yml (nouveau dossier)
name: Tests

# Quand lancer les tests
on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    name: Test Python ${{ matrix.python-version }}
    runs-on: ${{ matrix.os }}
    
    strategy:
      matrix:
        # Tester sur plusieurs versions Python et OS
        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
        os: [ubuntu-latest, windows-latest, macos-latest]
    
    steps:
    # 1. Checkout du code
    - uses: actions/checkout@v4
    
    # 2. Installer Python
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}
    
    # 3. Installer les dépendances
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -e ".[dev]"
    
    # 4. Lancer les tests
    - name: Run tests
      run: |
        pytest --cov=mathtools --cov-report=xml --cov-report=term
    
    # 5. Upload de la couverture de code
    - name: Upload coverage to Codecov
      if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
      uses: codecov/codecov-action@v3
      with:
        files: ./coverage.xml
        fail_ci_if_error: false

# ───────────────────────────────────────────────────────────────
# FICHIER: .github/workflows/publish.yml (publication automatique)
name: Publish to PyPI

on:
  release:
    types: [published]  # Déclenché lors de la création d'une release

jobs:
  publish:
    name: Publish to PyPI
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install build twine
    
    - name: Build package
      run: python -m build
    
    - name: Publish to PyPI
      env:
        TWINE_USERNAME: __token__
        TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
      run: twine upload dist/*

# CONFIGURATION:
# 1. Sur GitHub: Settings -> Secrets and variables -> Actions
# 2. New repository secret:
#    Name: PYPI_API_TOKEN
#    Secret: [Votre token PyPI]

# ───────────────────────────────────────────────────────────────
# FICHIER: .github/workflows/docs.yml (build automatique de la doc)
name: Documentation

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install dependencies
      run: |
        pip install -e ".[dev]"
    
    - name: Build documentation
      run: |
        cd docs
        make html
    
    - name: Deploy to GitHub Pages
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./docs/build/html

─────────────────────────────────────────────────────────────────
ÉTAPE 12.3: Configuration de pré-commit hooks
─────────────────────────────────────────────────────────────────

Vérifier le code avant chaque commit automatiquement.

# FICHIER: .pre-commit-config.yaml (à la racine)
repos:
  # Vérifications générales
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.5.0
    hooks:
      - id: trailing-whitespace    # Supprime espaces en fin de ligne
      - id: end-of-file-fixer      # Ajoute nouvelle ligne en fin de fichier
      - id: check-yaml             # Vérifie syntaxe YAML
      - id: check-json             # Vérifie syntaxe JSON
      - id: check-toml             # Vérifie syntaxe TOML
      - id: check-added-large-files  # Empêche gros fichiers
      - id: check-merge-conflict   # Détecte conflits de merge
      - id: debug-statements       # Détecte print() et pdb
  
  # Black (formatage Python)
  - repo: https://github.com/psf/black
    rev: 23.12.1
    hooks:
      - id: black
        language_version: python3.11
  
  # isort (tri des imports)
  - repo: https://github.com/PyCQA/isort
    rev: 5.13.2
    hooks:
      - id: isort
        args: ["--profile", "black"]
  
  # Flake8 (linting)
  - repo: https://github.com/PyCQA/flake8
    rev: 7.0.0
    hooks:
      - id: flake8
        args: ['--max-line-length=88', '--extend-ignore=E203']
  
  # mypy (vérification types)
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.8.0
    hooks:
      - id: mypy
        additional_dependencies: [types-all]

# INSTALLATION:
pip install pre-commit
pre-commit install

# À chaque commit, pre-commit vérifie automatiquement le code
# Si erreurs: le commit est bloqué jusqu'à correction

# Lancer manuellement sur tous les fichiers:
pre-commit run --all-files


═══════════════════════════════════════════════════════════════════
ÉTAPE 13: GESTION DES VERSIONS ET RELEASES
═══════════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────────────────
ÉTAPE 13.1: Utiliser bump2version pour gérer les versions
─────────────────────────────────────────────────────────────────

# Installation
pip install bump2version

# FICHIER: .bumpversion.cfg (à la racine)
[bumpversion]
current_version = 0.1.0
commit = True
tag = True
tag_name = v{new_version}
message = Bump version: {current_version} -> {new_version}

[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"

[bumpversion:file:mathtools/__init__.py]
search = __version__ = "{current_version}"
replace = __version__ = "{new_version}"

[bumpversion:file:docs/source/conf.py]
search = release = '{current_version}'
replace = release = '{new_version}'

# UTILISATION:
# Incrémenter PATCH (0.1.0 -> 0.1.1)
bump2version patch

# Incrémenter MINEUR (0.1.1 -> 0.2.0)
bump2version minor

# Incrémenter MAJEUR (0.2.0 -> 1.0.0)
bump2version major

# Cela:
# - Change la version dans tous les fichiers configurés
# - Crée un commit Git
# - Crée un tag Git
# - Pousse automatiquement si configuré

─────────────────────────────────────────────────────────────────
ÉTAPE 13.2: Créer une release sur GitHub
─────────────────────────────────────────────────────────────────

# 1. Après avoir bumper la version et pusher:
git push
git push --tags

# 2. Sur GitHub:
#    - Aller dans l'onglet "Releases"
#    - Cliquer "Create a new release"
#    - Choose a tag: v0.2.0 (votre nouveau tag)
#    - Release title: Version 0.2.0
#    - Description: (copier depuis CHANGELOG.md)

```markdown
## Version 0.2.0

### Ajouté
- Nouvelle fonction `power(base, exponent)`
- Support pour les configurations utilisateur
- Système de logging

### Amélioré
- Performance du calcul de factorielle (+15%)
- Documentation avec exemples interactifs

### Corrigé
- Bug dans add_numbers avec nombres flottants très grands
```

#    - Cocher "Set as the latest release" si c'est stable
#    - Cliquer "Publish release"

# 3. Si vous avez configuré GitHub Actions (voir ÉTAPE 12.2),
#    la release sera automatiquement publiée sur PyPI!

─────────────────────────────────────────────────────────────────
ÉTAPE 13.3: Gérer les branches de développement
─────────────────────────────────────────────────────────────────

STRATÉGIE GITFLOW (recommandée):

main (ou master)
  └─ Production, versions stables uniquement
  
develop
  └─ Développement, intégration des features
  
feature/nouvelle-fonction
  └─ Branches pour développer des fonctionnalités
  
hotfix/correction-bug
  └─ Corrections urgentes sur production
  
release/v1.0.0
  └─ Préparation des releases

# WORKFLOW:

# 1. Créer une branche de feature
git checkout develop
git checkout -b feature/power-function

# 2. Développer la fonctionnalité
# ... coder, tester ...
git add .
git commit -m "Add power function"

# 3. Merger dans develop
git checkout develop
git merge feature/power-function
git branch -d feature/power-function

# 4. Préparer une release
git checkout -b release/v0.2.0
# ... tests finaux, bump version ...
bump2version minor

# 5. Merger dans main ET develop
git checkout main
git merge release/v0.2.0
git tag v0.2.0

git checkout develop
git merge release/v0.2.0

# 6. Pousser
git push origin main develop --tags


═══════════════════════════════════════════════════════════════════
ÉTAPE 14: OPTIMISATIONS ET BONNES PRATIQUES
═══════════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────────────────
ÉTAPE 14.1: Type hints (annotations de types)
─────────────────────────────────────────────────────────────────

# FICHIER: mathtools/core.py (avec type hints)
"""Fonctions mathématiques avec annotations de types."""

from typing import Union, List, Optional

Number = Union[int, float]  # Alias de type

def add_numbers(a: Number, b: Number) -> Number:
    """
    Additionne deux nombres.
    
    Args:
        a: Premier nombre
        b: Deuxième nombre
        
    Returns:
        La somme de a et b
    """
    return a + b


def factorial(n: int) -> int:
    """
    Calcule la factorielle.
    
    Args:
        n: Nombre entier positif
        
    Returns:
        La factorielle de n
        
    Raises:
        ValueError: Si n est négatif
    """
    if n < 0:
        raise ValueError("n doit être positif")
    return 1 if n <= 1 else n * factorial(n - 1)


def process_numbers(
    numbers: List[Number],
    operation: str = "sum",
    precision: Optional[int] = None
) -> Number:
    """
    Traite une liste de nombres.
    
    Args:
        numbers: Liste de nombres à traiter
        operation: Opération à effectuer ('sum', 'product', 'mean')
        precision: Précision optionnelle pour l'arrondi
        
    Returns:
        Résultat de l'opération
    """
    if operation == "sum":
        result = sum(numbers)
    elif operation == "product":
        result = 1
        for n in numbers:
            result *= n
    elif operation == "mean":
        result = sum(numbers) / len(numbers)
    else:
        raise ValueError(f"Opération inconnue: {operation}")
    
    if precision is not None:
        result = round(result, precision)
    
    return result

# Vérifier les types avec mypy:
# mypy mathtools/

─────────────────────────────────────────────────────────────────
ÉTAPE 14.2: Docstrings au format Google/NumPy
─────────────────────────────────────────────────────────────────

# FORMAT GOOGLE (recommandé):
def example_google_style(arg1, arg2, arg3=None):
    """
    Description courte de la fonction.
    
    Description détaillée optionnelle sur plusieurs lignes
    qui explique ce que fait la fonction en détail.
    
    Args:
        arg1 (int): Description du premier argument.
        arg2 (str): Description du deuxième argument.
        arg3 (bool, optional): Description de l'argument optionnel.
            Defaults to None.
    
    Returns:
        dict: Description de ce qui est retourné.
            Peut être sur plusieurs lignes.
    
    Raises:
        ValueError: Quand arg2 est vide.
        TypeError: Quand arg1 n'est pas un entier.
    
    Examples:
        Exemples d'utilisation:
        
        >>> example_google_style(10, "test")
        {'result': 10}
        
        >>> example_google_style(5, "data", True)
        {'result': 5, 'debug': True}
    
    Note:
        Notes supplémentaires sur l'utilisation.
    
    Warning:
        Avertissements importants.
    
    See Also:
        other_function: Fonction similaire.
    """
    pass


# FORMAT NUMPY (alternatif):
def example_numpy_style(arg1, arg2, arg3=None):
    """
    Description courte de la fonction.
    
    Description détaillée optionnelle.
    
    Parameters
    ----------
    arg1 : int
        Description du premier argument.
    arg2 : str
        Description du deuxième argument.
    arg3 : bool, optional
        Description de l'argument optionnel (default: None).
    
    Returns
    -------
    dict
        Description de ce qui est retourné.
    
    Raises
    ------
    ValueError
        Quand arg2 est vide.
    
    Examples
    --------
    >>> example_numpy_style(10, "test")
    {'result': 10}
    """
    pass

─────────────────────────────────────────────────────────────────
ÉTAPE 14.3: Utiliser __all__ correctement
─────────────────────────────────────────────────────────────────

# FICHIER: mathtools/__init__.py
"""
MathTools - Utilitaires mathématiques.

Ce module exporte uniquement l'API publique.
"""

__version__ = "1.0.0"
__author__ = "Votre Nom"
__all__ = [
    # Fonctions principales (API publique)
    "add_numbers",
    "multiply_numbers",
    "factorial",
    "power",
    
    # Classes
    "Calculator",
    "MathConfig",
    
    # Exceptions
    "MathToolsError",
    "InvalidInputError",
    
    # Sous-modules
    "arithmetic",
    "geometry",
]

# Imports (ce qui est accessible)
from .core import add_numbers, multiply_numbers, factorial, power
from .calculator import Calculator
from .config import MathConfig
from .exceptions import MathToolsError, InvalidInputError
from . import arithmetic, geometry

# AVANTAGE:
# - from mathtools import * n'importe que ce qui est dans __all__
# - Clarifie l'API publique vs interne
# - Aide les IDEs pour l'autocomplétion

─────────────────────────────────────────────────────────────────
ÉTAPE 14.4: Caching et optimisations
─────────────────────────────────────────────────────────────────

# FICHIER: mathtools/optimized.py (nouveau)
"""Fonctions mathématiques optimisées."""

from functools import lru_cache, wraps
import time

# Cache LRU (Least Recently Used)
@lru_cache(maxsize=128)
def fibonacci(n: int) -> int:
    """
    Calcule le n-ième nombre de Fibonacci avec cache.
    
    Le cache garde les 128 derniers résultats.
    """
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# SANS cache: fibonacci(35) prend ~5 secondes
# AVEC cache: fibonacci(35) prend <0.001 secondes

# Voir les stats du cache:
# >>> fibonacci.cache_info()
# CacheInfo(hits=33, misses=36, maxsize=128, currsize=36)

# Vider le cache:
# >>> fibonacci.cache_clear()


# Décorateur de timing personnalisé
def timing_decorator(func):
    """Mesure le temps d'exécution d'une fonction."""
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        end = time.perf_counter()
        print(f"{func.__name__} a pris {end - start:.6f} secondes")
        return result
    return wrapper

@timing_decorator
def slow_computation(n):
    """Calcul lent pour démonstration."""
    return sum(i**2 for i in range(n))

# >>> slow_computation(1000000)
# slow_computation a pris 0.234567 secondes


# Cache personnalisé avec timeout
from datetime import datetime, timedelta

class TimedCache:
    """Cache avec expiration temporelle."""
    
    def __init__(self, timeout_seconds=60):
        self.cache = {}
        self.timeout = timedelta(seconds=timeout_seconds)
    
    def get(self, key):
        """Récupère une valeur du cache."""
        if key in self.cache:
            value, timestamp = self.cache[key]
            if datetime.now() - timestamp < self.timeout:
                return value
            else:
                del self.cache[key]  # Expirée
        return None
    
    def set(self, key, value):
        """Stocke une valeur dans le cache."""
        self.cache[key] = (value, datetime.now())

─────────────────────────────────────────────────────────────────
ÉTAPE 14.5: Compatibilité Python 3.7-3.12
─────────────────────────────────────────────────────────────────

# Gérer les différences entre versions Python

# FICHIER: mathtools/compat.py (nouveau)
"""Couche de compatibilité entre versions Python."""

import sys

# Vérifier la version Python
PY37 = sys.version_info >= (3, 7)
PY38 = sys.version_info >= (3, 8)
PY39 = sys.version_info >= (3, 9)
PY310 = sys.version_info >= (3, 10)

# Imports conditionnels
if PY39:
    from importlib.resources import files  # Python 3.9+
else:
    from importlib_resources import files  # Backport

if PY310:
    from typing import TypeAlias  # Python 3.10+
else:
    TypeAlias = type  # Fallback

# Fonction avec syntaxe compatible
def compatible_function(data):
    """Fonction compatible Python 3.7+."""
    # Python 3.10+ permet "match"/"case", mais 3.7-3.9 non
    # Utiliser if/elif à la place
    if isinstance(data, str):
        return data.upper()
    elif isinstance(data, int):
        return data * 2
    else:
        return None


═══════════════════════════════════════════════════════════════════
ÉTAPE 15: SÉCURITÉ ET MAINTENANCE
═══════════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────────────────
ÉTAPE 15.1: Scanner les vulnérabilités avec Safety et Bandit
─────────────────────────────────────────────────────────────────

# ═══════════════════════════════════════════════════════════════
# SAFETY - Scanner les vulnérabilités dans les dépendances
# ═══════════════════════════════════════════════════════════════

# Installation
pip install safety

# Scanner toutes les dépendances installées
safety check

# Exemple de sortie:
"""
╒══════════════════════════════════════════════════════════════════╕
│                                                                  │
│                               /$$$$$$            /$$             │
│                              /$$__  $$          | $$             │
│           /$$$$$$$  /$$$$$$ | $$  \__//$$$$$$  /$$$$$$   /$$   /$$│
│          /$$_____/ |____  $$| $$$$   /$$__  $$|_  $$_/  | $$  | $$│
│         |  $$$$$$   /$$$$$$$| $$_/  | $$$$$$$$  | $$    | $$  | $$│
│          \____  $$ /$$__  $$| $$    | $$_____/  | $$ /$$| $$  | $$│
│          /$$$$$$$/|  $$$$$$$| $$    |  $$$$$$$  |  $$$$/|  $$$$$$$│
│         |_______/  \_______/|__/     \_______/   \___/   \____  $$│
│                                                           /$$  | $$│
│                                                          |  $$$$$$/│
│  by pyup.io                                              \______/ │
│                                                                  │
╞══════════════════════════════════════════════════════════════════╡
│ REPORT                                                           │
│ checked 127 packages, using default DB                          │
╞════════════════════════════╤═══════════╤══════════════════════════╡
│ package                    │ installed │ affected                 │
│ version                    │ version   │ vulnerable?              │
╞════════════════════════════╪═══════════╪══════════════════════════╡
│ requests                   │ 2.25.0    │ <2.26.0                  │
╞════════════════════════════╧═══════════╧══════════════════════════╡
│ Vulnerability ID: 39525                                          │
│ CVSS v3 Base Score: 7.5 (HIGH)                                  │
│ Description: The package requests before 2.26.0 is vulnerable   │
│ to Exposure of Sensitive Information to an Unauthorized Actor   │
╘══════════════════════════════════════════════════════════════════╛

[ATTENTION]  1 vulnerability found
"""

# Scanner un fichier requirements.txt
safety check -r requirements.txt

# Format JSON pour intégration CI/CD
safety check --json

# Ignorer certaines vulnérabilités (temporaire)
safety check --ignore 39525

# Fichier de configuration: .safety-policy.yml
"""
# FICHIER: .safety-policy.yml
security:
  # Niveau de sévérité minimum pour échouer
  severity-high:
    fail: true
  severity-medium:
    fail: false
  
  # Vulnérabilités à ignorer (avec justification!)
  ignore-vulnerabilities:
    39525:
      reason: "Fixed in our fork, waiting for upstream release"
      expires: "2025-02-01"
"""

# ═══════════════════════════════════════════════════════════════
# BANDIT - Analyser le code source pour problèmes de sécurité
# ═══════════════════════════════════════════════════════════════

# Installation
pip install bandit

# Scanner tout le projet
bandit -r mathtools/

# Exemple de sortie:
"""
Run started:2025-01-15 14:30:45

Test results:
>> Issue: [B101:assert_used] Use of assert detected
   Severity: Low   Confidence: High
   Location: mathtools/core.py:45
   More Info: https://bandit.readthedocs.io/en/latest/plugins/b101_assert_used.html
44	    def validate(self, value):
45	        assert value > 0, "Value must be positive"
46	        return value

>> Issue: [B403:blacklist] Consider possible security implications with pickle
   Severity: Medium   Confidence: High
   Location: mathtools/cache.py:12
   More Info: https://bandit.readthedocs.io/en/latest/blacklists/blacklist_imports.html
11	import pickle
12	
13	def save_cache(data):
14	    with open('cache.pkl', 'wb') as f:
15	        pickle.dump(data, f)

Code scanned:
	Total lines of code: 423
	Total lines skipped (#nosec): 2

Run metrics:
	Total issues (by severity):
		Low: 5
		Medium: 2
		High: 0
	Total issues (by confidence):
		Low: 1
		Medium: 1
		High: 5
"""

# Options utiles
bandit -r mathtools/ -f json -o security-report.json  # Format JSON
bandit -r mathtools/ -ll  # Seulement Low et plus
bandit -r mathtools/ -lll # Seulement High
bandit -r mathtools/ --skip B101,B403  # Ignorer certains tests

# Configuration: .bandit (ou dans pyproject.toml)
"""
# FICHIER: .bandit
[bandit]
exclude_dirs = ['/tests/', '/venv/', '/.venv/']
skips = ['B101', 'B601']  # assert_used, paramiko_calls
tests = ['B201', 'B301']  # flask_debug, pickle
"""

# Dans pyproject.toml:
[tool.bandit]
exclude_dirs = ["tests", ".venv"]
skips = ["B101"]

# Ignorer une ligne spécifique
def use_pickle():
    import pickle  # nosec B403
    # Code qui doit vraiment utiliser pickle
    pass

# ═══════════════════════════════════════════════════════════════
# PIP-AUDIT - Alternative moderne à Safety
# ═══════════════════════════════════════════════════════════════

# Installation
pip install pip-audit

# Scanner les dépendances
pip-audit

# Scanner requirements.txt
pip-audit -r requirements.txt

# Format JSON
pip-audit --format json

# Corriger automatiquement (prudence!)
pip-audit --fix

# ═══════════════════════════════════════════════════════════════
# INTÉGRATION CI/CD - GitHub Actions
# ═══════════════════════════════════════════════════════════════

# FICHIER: .github/workflows/security.yml
name: Security Scan

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  schedule:
    # Scan quotidien à 2h du matin UTC
    - cron: '0 2 * * *'

jobs:
  security:
    name: Security Audit
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout code
      uses: actions/checkout@v4
    
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -e ".[dev]"
        pip install safety bandit pip-audit
    
    # Safety check
    - name: Run Safety check
      run: |
        safety check --json --output safety-report.json || true
        cat safety-report.json
      continue-on-error: true
    
    # Bandit check
    - name: Run Bandit security scan
      run: |
        bandit -r mathtools/ -f json -o bandit-report.json
    
    # pip-audit check
    - name: Run pip-audit
      run: |
        pip-audit --format json --output pip-audit-report.json || true
      continue-on-error: true
    
    # Upload rapports comme artifacts
    - name: Upload security reports
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: security-reports
        path: |
          *-report.json
    
    # Commenter la PR avec résultats (optionnel)
    - name: Comment PR with results
      if: github.event_name == 'pull_request'
      uses: actions/github-script@v6
      with:
        script: |
          const fs = require('fs');
          const safety = JSON.parse(fs.readFileSync('safety-report.json'));
          const comment = `## [VERROUILLE] Security Scan Results
          
          **Safety**: ${safety.vulnerabilities.length} vulnerabilities found
          
          See artifacts for detailed reports.`;
          
          github.rest.issues.createComment({
            issue_number: context.issue.number,
            owner: context.repo.owner,
            repo: context.repo.repo,
            body: comment
          });

─────────────────────────────────────────────────────────────────
ÉTAPE 15.2: Créer un fichier SECURITY.md complet
─────────────────────────────────────────────────────────────────

# FICHIER: SECURITY.md (à la racine du projet)
# Ce fichier apparaîtra dans l'onglet "Security" de GitHub

```markdown
# Politique de Sécurité

## [VERROUILLE] Versions Supportées

Nous fournissons des correctifs de sécurité pour les versions suivantes:

| Version | Supportée          | Fin de support |
| ------- | ------------------ | -------------- |
| 2.x.x   | [OK] Oui             | -              |
| 1.9.x   | [OK] Oui             | 2025-12-31     |
| 1.8.x   | [ATTENTION]  Critique uniquement | 2025-06-30     |
| < 1.8   | [X] Non             | -              |

**Note**: Nous recommandons fortement de toujours utiliser la dernière version stable.

## [BUG] Signaler une Vulnérabilité

### Processus de Divulgation Responsable

Si vous découvrez une vulnérabilité de sécurité dans MathTools, merci de suivre ces étapes:

### 1. NE PAS Créer d'Issue Publique

Les vulnérabilités de sécurité ne doivent **JAMAIS** être divulguées publiquement avant d'être corrigées.

### 2. Nous Contacter en Privé

**Email de sécurité**: security@mathtools.example.com

Ou utiliser le formulaire de GitHub:
- Aller sur l'onglet "Security" du repository
- Cliquer sur "Report a vulnerability"
- Remplir le formulaire

### 3. Informations à Inclure

Veuillez fournir autant de détails que possible:

```
[LISTE] Template de rapport de vulnérabilité:

**Description de la vulnérabilité**
[Décrivez le problème de sécurité]

**Type de vulnérabilité**
[ ] Injection de code
[ ] Cross-Site Scripting (XSS)
[ ] SQL Injection
[ ] Déni de service (DoS)
[ ] Exposition de données sensibles
[ ] Autre: ___________

**Versions affectées**
[Listez les versions concernées]

**Étapes pour reproduire**
1. [Première étape]
2. [Deuxième étape]
3. [...]

**Impact potentiel**
[Décrivez les conséquences possibles]

**Preuve de concept**
[Code ou commandes pour reproduire, si applicable]

**Environnement**
- OS: [Linux/Windows/macOS]
- Python: [version]
- MathTools: [version]

**Suggestions de correction**
[Si vous avez des idées, partagez-les]

**Votre contact**
- Nom/Pseudo: [optionnel]
- Email: [pour communications futures]
- Souhaitez-vous être crédité? [Oui/Non]
```

### 4. Délai de Réponse

- **Confirmation initiale**: sous 48 heures
- **Évaluation préliminaire**: sous 7 jours
- **Mise à jour complète**: toutes les 2 semaines jusqu'à résolution

### 5. Processus de Correction

```
┌─────────────────┐
│ 1. Réception    │ Nous accusons réception
└────────┬────────┘
         v
┌─────────────────┐
│ 2. Triage       │ Évaluation de la gravité (24-48h)
└────────┬────────┘
         v
┌─────────────────┐
│ 3. Investigation│ Analyse approfondie (1-7 jours)
└────────┬────────┘
         v
┌─────────────────┐
│ 4. Développement│ Création du correctif (variable)
└────────┬────────┘
         v
┌─────────────────┐
│ 5. Tests        │ Validation du correctif
└────────┬────────┘
         v
┌─────────────────┐
│ 6. Publication  │ Release de sécurité
└────────┬────────┘
         v
┌─────────────────┐
│ 7. Divulgation  │ Annonce publique coordonnée
└─────────────────┘
```

### 6. Niveaux de Gravité

Nous utilisons le système CVSS (Common Vulnerability Scoring System):

| Score CVSS | Gravité  | Délai de correction |
|-----------|----------|---------------------|
| 9.0-10.0  | [ROUGE] Critique | 1-7 jours          |
| 7.0-8.9   | [ORANGE] Élevée   | 7-30 jours         |
| 4.0-6.9   | [JAUNE] Moyenne  | 30-90 jours        |
| 0.1-3.9   | [VERT] Faible   | 90+ jours          |

### 7. Divulgation Publique

Après la publication du correctif:

1. **Advisory GitHub** créé avec détails complets
2. **CVE** demandé si applicable
3. **Annonce** sur notre blog/Twitter
4. **Email** aux mainteneurs de packages dépendants

**Embargo**: Nous demandons un délai de 90 jours avant divulgation publique complète.

## [TROPHEE] Hall of Fame - Chercheurs en Sécurité

Nous remercions les personnes suivantes pour leurs contributions à la sécurité de MathTools:

| Date | Chercheur | Vulnérabilité | Gravité |
|------|-----------|---------------|---------|
| 2025-01 | @security_pro | Input validation bypass | Moyenne |
| 2024-12 | Jane Doe | DoS vulnerability | Élevée |

*Voulez-vous être mentionné ici? Aidez-nous à sécuriser MathTools!*

## [SECURITE] Bonnes Pratiques pour les Utilisateurs

### Installation Sécurisée

```bash
# [OK] RECOMMANDÉ: Toujours vérifier les checksums
pip install mathtools --require-hashes

# [OK] RECOMMANDÉ: Utiliser un environnement virtuel
python -m venv .venv
source .venv/bin/activate
pip install mathtools

# [ATTENTION]  ATTENTION: Ne jamais installer avec sudo
sudo pip install mathtools  # [X] DANGEREUX!
```

### Mises à Jour Régulières

```bash
# Vérifier les mises à jour de sécurité
pip list --outdated

# Mettre à jour MathTools
pip install --upgrade mathtools

# Vérifier la version installée
pip show mathtools
```

### Validation des Données

Toujours valider les données d'entrée dans votre code:

```python
from mathtools import factorial
from mathtools.exceptions import InvalidInputError

def safe_compute(user_input):
    try:
        # Valider avant d'utiliser
        n = int(user_input)
        if not 0 <= n <= 1000:
            raise ValueError("Input out of range")
        
        return factorial(n)
    except (ValueError, InvalidInputError) as e:
        # Gérer proprement
        print(f"Invalid input: {e}")
        return None
```

## [DOCS] Ressources Supplémentaires

- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [Python Security Best Practices](https://python.readthedocs.io/en/stable/library/security_warnings.html)
- [Snyk Python Security](https://snyk.io/blog/python-security-best-practices-cheat-sheet/)

## [EMAIL] Contact

- **Email de sécurité**: security@mathtools.example.com
- **PGP Key**: [Lien vers clé publique]
- **Security Policy**: Ce document
- **Bug Bounty**: Actuellement, nous n'avons pas de programme de primes

## [DOC] Historique des Vulnérabilités

Consultez notre [Security Advisories](https://github.com/username/mathtools/security/advisories) pour l'historique complet.

---

*Dernière mise à jour: 2025-01-15*
*Version de cette politique: 1.2*
```

─────────────────────────────────────────────────────────────────
ÉTAPE 15.3: Bonnes pratiques de sécurité dans le code
─────────────────────────────────────────────────────────────────

# ═══════════════════════════════════════════════════════════════
# VALIDATION DES ENTRÉES
# ═══════════════════════════════════════════════════════════════

# FICHIER: mathtools/validators.py (nouveau)
"""Validation sécurisée des entrées utilisateur."""

import re
from typing import Any, Union
from .exceptions import InvalidInputError

class InputValidator:
    """Validateur centralisé pour toutes les entrées."""
    
    @staticmethod
    def validate_integer(
        value: Any,
        min_value: int = None,
        max_value: int = None,
        param_name: str = "value"
    ) -> int:
        """
        Valide qu'une valeur est un entier dans une plage.
        
        Args:
            value: Valeur à valider
            min_value: Valeur minimale (incluse)
            max_value: Valeur maximale (incluse)
            param_name: Nom du paramètre pour messages d'erreur
            
        Returns:
            int: Valeur validée
            
        Raises:
            InvalidInputError: Si validation échoue
        """
        # Vérifier le type
        if not isinstance(value, int):
            # Attention aux booléens (isinstance(True, int) == True)
            if isinstance(value, bool):
                raise InvalidInputError(
                    f"{param_name} must be an integer, not boolean"
                )
            raise InvalidInputError(
                f"{param_name} must be an integer, got {type(value).__name__}"
            )
        
        # Vérifier la plage
        if min_value is not None and value < min_value:
            raise InvalidInputError(
                f"{param_name} must be >= {min_value}, got {value}"
            )
        
        if max_value is not None and value > max_value:
            raise InvalidInputError(
                f"{param_name} must be <= {max_value}, got {value}"
            )
        
        return value
    
    @staticmethod
    def validate_string(
        value: Any,
        min_length: int = None,
        max_length: int = None,
        pattern: str = None,
        param_name: str = "value"
    ) -> str:
        """
        Valide qu'une valeur est une chaîne valide.
        
        Args:
            value: Valeur à valider
            min_length: Longueur minimale
            max_length: Longueur maximale
            pattern: Pattern regex à matcher
            param_name: Nom du paramètre
            
        Returns:
            str: Valeur validée
            
        Raises:
            InvalidInputError: Si validation échoue
        """
        if not isinstance(value, str):
            raise InvalidInputError(
                f"{param_name} must be a string, got {type(value).__name__}"
            )
        
        if min_length is not None and len(value) < min_length:
            raise InvalidInputError(
                f"{param_name} must be at least {min_length} characters"
            )
        
        if max_length is not None and len(value) > max_length:
            raise InvalidInputError(
                f"{param_name} must be at most {max_length} characters"
            )
        
        if pattern is not None and not re.match(pattern, value):
            raise InvalidInputError(
                f"{param_name} does not match required pattern"
            )
        
        return value
    
    @staticmethod
    def sanitize_path(path: str) -> str:
        """
        Sanitize un chemin de fichier pour éviter path traversal.
        
        Args:
            path: Chemin à sanitizer
            
        Returns:
            str: Chemin sécurisé
            
        Raises:
            InvalidInputError: Si chemin suspect
        """
        from pathlib import Path
        
        # Convertir en Path pour normalisation
        path_obj = Path(path)
        
        # Vérifier path traversal
        if ".." in path_obj.parts:
            raise InvalidInputError(
                "Path traversal detected (..)"
            )
        
        # Vérifier chemins absolus non autorisés
        if path_obj.is_absolute():
            raise InvalidInputError(
                "Absolute paths not allowed"
            )
        
        return str(path_obj)


# ═══════════════════════════════════════════════════════════════
# UTILISATION DANS LE CODE
# ═══════════════════════════════════════════════════════════════

# FICHIER: mathtools/core.py (avec validation)
from .validators import InputValidator
from .exceptions import InvalidInputError

def factorial(n: int) -> int:
    """
    Calcule la factorielle avec validation stricte.
    
    Args:
        n: Entier positif (0 <= n <= 10000)
        
    Returns:
        La factorielle de n
        
    Raises:
        InvalidInputError: Si n invalide
    """
    # Validation sécurisée
    n = InputValidator.validate_integer(
        n,
        min_value=0,
        max_value=10000,  # Limite pour éviter DoS
        param_name="n"
    )
    
    # Calcul sécurisé
    if n <= 1:
        return 1
    
    result = 1
    for i in range(2, n + 1):
        result *= i
    
    return result


# ═══════════════════════════════════════════════════════════════
# PROTECTION CONTRE L'INJECTION DE CODE
# ═══════════════════════════════════════════════════════════════

def evaluate_expression_unsafe(expr: str) -> float:
    """
    [X] DANGEREUX - NE JAMAIS FAIRE ÇA!
    eval() permet l'exécution de code arbitraire!
    """
    return eval(expr)  # [ALERTE] VULNÉRABILITÉ CRITIQUE!

# L'utilisateur pourrait faire:
# evaluate_expression_unsafe("__import__('os').system('rm -rf /')")


def evaluate_expression_safe(expr: str) -> float:
    """
    [OK] SÉCURISÉ - Parser proprement les expressions.
    """
    import ast
    import operator
    
    # Opérateurs autorisés
    OPERATORS = {
        ast.Add: operator.add,
        ast.Sub: operator.sub,
        ast.Mult: operator.mul,
        ast.Div: operator.truediv,
        ast.Pow: operator.pow,
    }
    
    def eval_node(node):
        """Évalue un noeud AST de manière sécurisée."""
        if isinstance(node, ast.Num):  # Nombre
            return node.n
        elif isinstance(node, ast.BinOp):  # Opération binaire
            op = OPERATORS.get(type(node.op))
            if op is None:
                raise InvalidInputError(f"Operator not allowed: {node.op}")
            return op(eval_node(node.left), eval_node(node.right))
        else:
            raise InvalidInputError(f"Expression type not allowed: {type(node)}")
    
    try:
        tree = ast.parse(expr, mode='eval')
        return eval_node(tree.body)
    except Exception as e:
        raise InvalidInputError(f"Invalid expression: {e}")

# Utilisation:
# evaluate_expression_safe("2 + 3 * 4")  # [OK] OK: 14
# evaluate_expression_safe("__import__('os')")  # [X] Erreur!


# ═══════════════════════════════════════════════════════════════
# GESTION SÉCURISÉE DES FICHIERS
# ═══════════════════════════════════════════════════════════════

def load_data_unsafe(filename: str):
    """[X] DANGEREUX - Vulnérable au path traversal"""
    with open(filename, 'r') as f:  # [ALERTE] DANGER!
        return f.read()

# Attaquant pourrait faire:
# load_data_unsafe("../../etc/passwd")


def load_data_safe(filename: str, base_dir: str = "./data"):
    """[OK] SÉCURISÉ - Validation du chemin"""
    from pathlib import Path
    
    # Sanitize le filename
    filename = InputValidator.sanitize_path(filename)
    
    # Construire le chemin complet
    base_path = Path(base_dir).resolve()
    full_path = (base_path / filename).resolve()
    
    # Vérifier que le fichier est bien dans base_dir
    if not full_path.is_relative_to(base_path):
        raise InvalidInputError(
            "Access denied: file outside allowed directory"
        )
    
    # Vérifier que le fichier existe
    if not full_path.exists():
        raise FileNotFoundError(f"File not found: {filename}")
    
    # Vérifier la taille (protection DoS)
    max_size = 10 * 1024 * 1024  # 10 MB
    if full_path.stat().st_size > max_size:
        raise InvalidInputError(
            f"File too large (max: {max_size} bytes)"
        )
    
    # Lecture sécurisée
    with open(full_path, 'r', encoding='utf-8') as f:
        return f.read()


# ═══════════════════════════════════════════════════════════════
# PROTECTION CONTRE LES DÉNIS DE SERVICE (DoS)
# ═══════════════════════════════════════════════════════════════

import time
from functools import wraps
from collections import defaultdict
from threading import Lock

class RateLimiter:
    """Limiteur de taux pour prévenir les abus."""
    
    def __init__(self, max_calls: int, period: float):
        """
        Args:
            max_calls: Nombre maximum d'appels
            period: Période en secondes
        """
        self.max_calls = max_calls
        self.period = period
        self.calls = defaultdict(list)
        self.lock = Lock()
    
    def __call__(self, func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            with self.lock:
                now = time.time()
                key = id(func)  # Identifiant de la fonction
                
                # Nettoyer les vieux appels
                self.calls[key] = [
                    t for t in self.calls[key]
                    if now - t < self.period
                ]
                
                # Vérifier la limite
                if len(self.calls[key]) >= self.max_calls:
                    raise Exception(
                        f"Rate limit exceeded: max {self.max_calls} "
                        f"calls per {self.period}s"
                    )
                
                # Enregistrer l'appel
                self.calls[key].append(now)
            
            return func(*args, **kwargs)
        
        return wrapper

# Utilisation:
@RateLimiter(max_calls=100, period=60)  # 100 appels/minute max
def expensive_computation(n: int) -> int:
    """Fonction coûteuse protégée par rate limiting."""
    return factorial(n)


def timeout_decorator(seconds: int):
    """Décorateur pour limiter le temps d'exécution."""
    import signal
    
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            def timeout_handler(signum, frame):
                raise TimeoutError(
                    f"Function {func.__name__} exceeded {seconds}s timeout"
                )
            
            # Configurer le timeout (Unix seulement)
            signal.signal(signal.SIGALRM, timeout_handler)
            signal.alarm(seconds)
            
            try:
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)  # Annuler le timeout
            
            return result
        
        return wrapper
    
    return decorator

# Utilisation:
@timeout_decorator(5)  # Max 5 secondes
def slow_function(n: int):
    """Fonction avec timeout pour éviter blocage."""
    return sum(i**2 for i in range(n))


# ═══════════════════════════════════════════════════════════════
# LOGGING SÉCURISÉ
# ═══════════════════════════════════════════════════════════════

import logging
from typing import Any

def safe_log(logger: logging.Logger, level: int, message: str, **kwargs):
    """
    Log sécurisé qui filtre les données sensibles.
    
    Args:
        logger: Logger à utiliser
        level: Niveau de log (INFO, WARNING, etc.)
        message: Message à logger
        **kwargs: Données additionnelles
    """
    # Filtrer les données sensibles
    SENSITIVE_KEYS = {
        'password', 'passwd', 'pwd',
        'token', 'api_key', 'secret',
        'credit_card', 'ssn'
    }
    
    filtered_kwargs = {}
    for key, value in kwargs.items():
        if any(sensitive in key.lower() for sensitive in SENSITIVE_KEYS):
            filtered_kwargs[key] = '***REDACTED***'
        else:
            filtered_kwargs[key] = value
    
    # Logger avec données filtrées
    logger.log(level, message, extra=filtered_kwargs)

# Utilisation:
logger = logging.getLogger(__name__)

# [X] DANGEREUX
logger.info(f"User login: {username} with password: {password}")

# [OK] SÉCURISÉ
safe_log(
    logger, 
    logging.INFO,
    "User login attempt",
    username=username,
    password=password  # Sera automatiquement redacted
)


# ═══════════════════════════════════════════════════════════════
# SÉRIALISATION SÉCURISÉE
# ═══════════════════════════════════════════════════════════════

# [X] DANGEREUX - pickle peut exécuter du code
import pickle

def save_data_unsafe(data, filename):
    """NE JAMAIS UTILISER pickle avec des données non fiables!"""
    with open(filename, 'wb') as f:
        pickle.dump(data, f)  # [ALERTE] VULNÉRABILITÉ!


# [OK] SÉCURISÉ - Utiliser JSON à la place
import json

def save_data_safe(data: dict, filename: str):
    """Sérialisation sécurisée avec JSON."""
    # Valider que data est JSON-sérialisable
    try:
        json_str = json.dumps(data)
    except (TypeError, ValueError) as e:
        raise InvalidInputError(f"Data not JSON serializable: {e}")
    
    # Sauvegarder de manière sécurisée
    filepath = InputValidator.sanitize_path(filename)
    with open(filepath, 'w', encoding='utf-8') as f:
        f.write(json_str)

def load_data_safe(filename: str) -> dict:
    """Chargement sécurisé depuis JSON."""
    filepath = InputValidator.sanitize_path(filename)
    
    with open(filepath, 'r', encoding='utf-8') as f:
        try:
            data = json.load(f)
        except json.JSONDecodeError as e:
            raise InvalidInputError(f"Invalid JSON: {e}")
    
    return data


─────────────────────────────────────────────────────────────────
ÉTAPE 15.4: Gestion des secrets et variables d'environnement
─────────────────────────────────────────────────────────────────

# ═══════════════════════════════════════════════════════════════
# UTILISER python-dotenv POUR LES SECRETS
# ═══════════════════════════════════════════════════════════════

# Installation
pip install python-dotenv

# FICHIER: .env (local, JAMAIS commité!)
"""
# Configuration locale (development)
MATHTOOLS_API_KEY=sk_test_123456789
MATHTOOLS_DB_PASSWORD=super_secret_password
MATHTOOLS_DEBUG=true
MATHTOOLS_LOG_LEVEL=DEBUG
"""

# FICHIER: .env.example (commité dans Git)
"""
# Template de configuration - copier vers .env
MATHTOOLS_API_KEY=your_api_key_here
MATHTOOLS_DB_PASSWORD=your_password_here
MATHTOOLS_DEBUG=false
MATHTOOLS_LOG_LEVEL=INFO
"""

# FICHIER: mathtools/config.py (lecture des secrets)
import os
from pathlib import Path
from typing import Optional
from dotenv import load_dotenv

class Config:
    """Configuration avec support des variables d'environnement."""
    
    def __init__(self):
        # Charger .env si présent
        env_path = Path('.env')
        if env_path.exists():
            load_dotenv(env_path)
        
        # Charger la configuration
        self._load_config()
    
    def _load_config(self):
        """Charge toute la configuration."""
        # API Keys (obligatoires en production)
        self.api_key = self._get_required('MATHTOOLS_API_KEY')
        
        # Database
        self.db_password = self._get_required('MATHTOOLS_DB_PASSWORD')
        
        # Options (avec valeurs par défaut)
        self.debug = self._get_bool('MATHTOOLS_DEBUG', False)
        self.log_level = os.getenv('MATHTOOLS_LOG_LEVEL', 'INFO')
        self.max_retries = self._get_int('MATHTOOLS_MAX_RETRIES', 3)
    
    def _get_required(self, key: str) -> str:
        """Récupère une variable obligatoire."""
        value = os.getenv(key)
        if not value:
            raise EnvironmentError(
                f"Required environment variable '{key}' not set. "
                f"See .env.example for template."
            )
        return value
    
    def _get_bool(self, key: str, default: bool = False) -> bool:
        """Récupère une variable booléenne."""
        value = os.getenv(key, str(default)).lower()
        return value in ('true', '1', 'yes', 'on')
    
    def _get_int(self, key: str, default: int) -> int:
        """Récupère une variable entière."""
        value = os.getenv(key)
        if value is None:
            return default
        try:
            return int(value)
        except ValueError:
            raise EnvironmentError(
                f"Environment variable '{key}' must be an integer, got '{value}'"
            )
    
    def is_production(self) -> bool:
        """Vérifie si on est en production."""
        env = os.getenv('MATHTOOLS_ENV', 'development').lower()
        return env == 'production'
    
    def mask_secret(self, secret: str) -> str:
        """Masque un secret pour l'affichage."""
        if len(secret) <= 8:
            return '***'
        return secret[:4] + '***' + secret[-4:]

# Instance globale
config = Config()

# Utilisation:
# from mathtools.config import config
# api_key = config.api_key
# if config.debug:
#     print(f"Using API key: {config.mask_secret(api_key)}")


# ═══════════════════════════════════════════════════════════════
# SECRETS DANS LES TESTS
# ═══════════════════════════════════════════════════════════════

# FICHIER: tests/conftest.py
import pytest
import os

@pytest.fixture(scope='session', autouse=True)
def setup_test_env():
    """Configure l'environnement de test avec des secrets fictifs."""
    # Sauvegarder les vraies valeurs
    original_env = dict(os.environ)
    
    # Injecter des valeurs de test
    os.environ['MATHTOOLS_API_KEY'] = 'test_key_12345'
    os.environ['MATHTOOLS_DB_PASSWORD'] = 'test_password'
    os.environ['MATHTOOLS_DEBUG'] = 'true'
    os.environ['MATHTOOLS_ENV'] = 'testing'
    
    yield
    
    # Restaurer l'environnement original
    os.environ.clear()
    os.environ.update(original_env)

# FICHIER: tests/test_config.py
def test_config_loads_from_env():
    """Test que la config charge depuis l'environnement."""
    from mathtools.config import Config
    
    config = Config()
    assert config.api_key == 'test_key_12345'
    assert config.debug is True
    assert config.is_production() is False


# ═══════════════════════════════════════════════════════════════
# SECRETS DANS CI/CD (GitHub Actions)
# ═══════════════════════════════════════════════════════════════

# 1. Sur GitHub: Settings -> Secrets and variables -> Actions
# 2. Ajouter les secrets:
#    - PYPI_API_TOKEN
#    - CODECOV_TOKEN
#    - etc.

# FICHIER: .github/workflows/tests.yml
name: Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    env:
      # Variables publiques (non sensibles)
      MATHTOOLS_DEBUG: "false"
      MATHTOOLS_LOG_LEVEL: "INFO"
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install dependencies
      run: |
        pip install -e ".[dev]"
    
    - name: Run tests
      env:
        # Secrets depuis GitHub Secrets
        MATHTOOLS_API_KEY: ${{ secrets.TEST_API_KEY }}
        MATHTOOLS_DB_PASSWORD: ${{ secrets.TEST_DB_PASSWORD }}
      run: |
        pytest --cov=mathtools
    
    - name: Upload coverage
      env:
        CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
      run: |
        codecov


# ═══════════════════════════════════════════════════════════════
# DÉTECTER LES SECRETS ACCIDENTELLEMENT COMMITÉS
# ═══════════════════════════════════════════════════════════════

# Installation de gitleaks (scanner de secrets)
# https://github.com/gitleaks/gitleaks

# Installer gitleaks (MacOS)
brew install gitleaks

# Installer gitleaks (Linux)
wget https://github.com/gitleaks/gitleaks/releases/download/v8.18.0/gitleaks_8.18.0_linux_x64.tar.gz
tar -xzf gitleaks_8.18.0_linux_x64.tar.gz
sudo mv gitleaks /usr/local/bin/

# Scanner le repository
gitleaks detect --verbose

# Scanner avant de commit (pre-commit hook)
gitleaks protect --verbose --staged

# FICHIER: .gitleaks.toml (configuration)
title = "MathTools Gitleaks Config"

[extend]
useDefault = true

[[rules]]
id = "mathtools-api-key"
description = "MathTools API Key"
regex = '''sk_(test|live)_[0-9a-zA-Z]{20,}'''
keywords = [
    "mathtools_api_key",
    "MATHTOOLS_API_KEY",
]

[[rules]]
id = "generic-secret"
description = "Generic Secret"
regex = '''(?i)(secret|password|passwd|pwd|token|api[_-]?key)\s*[:=]\s*['"][^'"]{8,}['"]'''

[allowlist]
description = "Allowlist"
paths = [
    '''.env.example''',  # Template, pas de vrais secrets
    '''README.md''',     # Documentation
]


# Intégrer dans pre-commit
# FICHIER: .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.0
    hooks:
      - id: gitleaks


─────────────────────────────────────────────────────────────────
ÉTAPE 15.5: Configurer Dependabot pour mises à jour automatiques
─────────────────────────────────────────────────────────────────

# FICHIER: .github/dependabot.yml (configuration complète)
version: 2

# Configuration pour recevoir les notifications
# (GitHub enverra des emails pour chaque PR Dependabot)

updates:
  # ═════════════════════════════════════════════════════════════
  # Dépendances Python (pip)
  # ═════════════════════════════════════════════════════════════
  - package-ecosystem: "pip"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
      timezone: "UTC"
    
    # Limites
    open-pull-requests-limit: 10
    
    # Labels pour les PRs
    labels:
      - "dependencies"
      - "python"
      - "security"
    
    # Reviewer automatique
    reviewers:
      - "votre-username"
    
    # Préfixe des commits
    commit-message:
      prefix: "deps"
      prefix-development: "deps-dev"
      include: "scope"
    
    # Grouper les mises à jour mineures
    groups:
      development-dependencies:
        dependency-type: "development"
        update-types:
          - "minor"
          - "patch"
      
      production-dependencies:
        dependency-type: "production"
        update-types:
          - "patch"
    
    # Ignorer certains packages
    ignore:
      - dependency-name: "numpy"
        versions: ["2.x"]  # Rester sur 1.x pour compatibilité
    
    # Autoriser les versions instables pour dev
    allow:
      - dependency-type: "development"
    
    # Assignees
    assignees:
      - "votre-username"
  
  # ═════════════════════════════════════════════════════════════
  # GitHub Actions
  # ═════════════════════════════════════════════════════════════
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
    
    labels:
      - "dependencies"
      - "github-actions"
    
    commit-message:
      prefix: "ci"
    
    # Grouper toutes les actions ensemble
    groups:
      github-actions:
        patterns:
          - "*"
  
  # ═════════════════════════════════════════════════════════════
  # Docker (si vous utilisez Docker)
  # ═════════════════════════════════════════════════════════════
  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"
    
    labels:
      - "dependencies"
      - "docker"


# ═══════════════════════════════════════════════════════════════
# WORKFLOW POUR AUTO-MERGER LES MISES À JOUR MINEURES
# ═══════════════════════════════════════════════════════════════

# FICHIER: .github/workflows/dependabot-auto-merge.yml
name: Dependabot Auto-Merge

on:
  pull_request:
    types: [opened, synchronize, reopened]

permissions:
  contents: write
  pull-requests: write

jobs:
  auto-merge:
    name: Auto-merge Dependabot PRs
    runs-on: ubuntu-latest
    
    # Seulement pour les PRs de Dependabot
    if: github.actor == 'dependabot[bot]'
    
    steps:
    - name: Fetch Dependabot metadata
      id: metadata
      uses: dependabot/fetch-metadata@v1
      with:
        github-token: "${{ secrets.GITHUB_TOKEN }}"
    
    - name: Auto-merge patch updates
      # Merger automatiquement les updates patch (ex: 1.2.3 -> 1.2.4)
      if: steps.metadata.outputs.update-type == 'version-update:semver-patch'
      run: |
        gh pr review ${{ github.event.pull_request.number }} --approve
        gh pr merge ${{ github.event.pull_request.number }} --auto --squash
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    
    - name: Auto-approve minor dev updates
      # Approuver (mais ne pas merger) les updates mineures en dev
      if: |
        steps.metadata.outputs.update-type == 'version-update:semver-minor' &&
        steps.metadata.outputs.dependency-type == 'direct:development'
      run: |
        gh pr review ${{ github.event.pull_request.number }} --approve
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    
    - name: Comment on major updates
      # Commenter sur les updates majeures (nécessitent revue manuelle)
      if: steps.metadata.outputs.update-type == 'version-update:semver-major'
      uses: actions/github-script@v6
      with:
        script: |
          github.rest.issues.createComment({
            issue_number: context.issue.number,
            owner: context.repo.owner,
            repo: context.repo.repo,
            body: '[ATTENTION] **Major version update** - Manual review required before merging!'
          })


# ═══════════════════════════════════════════════════════════════
# MONITORING DES VULNÉRABILITÉS AVEC DEPENDABOT ALERTS
# ═══════════════════════════════════════════════════════════════

# Sur GitHub:
# 1. Settings -> Security -> Code security and analysis
# 2. Activer:
#    [OK] Dependency graph
#    [OK] Dependabot alerts
#    [OK] Dependabot security updates

# Recevoir les alertes:
# 1. Settings -> Notifications
# 2. Watching -> Security alerts -> Web + Email

# Dependabot créera automatiquement des PRs pour les vulnérabilités de sécurité!


─────────────────────────────────────────────────────────────────
ÉTAPE 15.6: Audit de sécurité complet avec pip-audit
─────────────────────────────────────────────────────────────────

# Installation
pip install pip-audit

# ═══════════════════════════════════════════════════════════════
# UTILISATION DE BASE
# ═══════════════════════════════════════════════════════════════

# Auditer les dépendances installées
pip-audit

# Auditer un fichier requirements
pip-audit -r requirements.txt

# Format JSON pour CI/CD
pip-audit --format json --output audit-report.json

# Format Markdown pour documentation
pip-audit --format markdown

# Sortie cyclonedx SBOM (Software Bill of Materials)
pip-audit --format cyclonedx-json --output sbom.json

# ═══════════════════════════════════════════════════════════════
# OPTIONS AVANCÉES
# ═══════════════════════════════════════════════════════════════

# Corriger automatiquement (PRUDENCE!)
pip-audit --fix

# Mode dry-run (voir ce qui serait corrigé)
pip-audit --fix --dry-run

# Scanner avec une base de données spécifique
pip-audit --vulnerability-service osv  # OSV (recommandé)
pip-audit --vulnerability-service pypi # PyPI Advisory Database

# Spécifier un niveau de sévérité minimum
pip-audit --require-hashes  # Vérifier les hashes

# Ignorer certaines vulnérabilités
pip-audit --ignore-vuln GHSA-xxxx-yyyy-zzzz

# ═══════════════════════════════════════════════════════════════
# INTÉGRATION CI/CD
# ═══════════════════════════════════════════════════════════════

# FICHIER: .github/workflows/audit.yml
name: Security Audit

on:
  push:
    branches: [main]
  pull_request:
  schedule:
    # Audit quotidien à 3h du matin
    - cron: '0 3 * * *'

jobs:
  audit:
    name: Pip Audit
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11'
    
    - name: Install pip-audit
      run: pip install pip-audit
    
    - name: Run pip-audit
      run: |
        pip-audit \
          --format json \
          --output audit-report.json \
          --vulnerability-service osv
      continue-on-error: true
    
    - name: Upload audit report
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: audit-report
        path: audit-report.json
    
    - name: Check for vulnerabilities
      run: |
        # Échouer si des vulnérabilités critiques sont trouvées
        if grep -q '"severity": "CRITICAL"' audit-report.json; then
          echo "[X] Critical vulnerabilities found!"
          exit 1
        fi


# ═══════════════════════════════════════════════════════════════
# GÉNÉRER UN SBOM (Software Bill of Materials)
# ═══════════════════════════════════════════════════════════════

# SBOM = Liste complète de tous les composants du logiciel
# Utile pour audits de sécurité et conformité

# Générer SBOM CycloneDX
pip-audit --format cyclonedx-json --output sbom.cyclonedx.json

# Générer SBOM SPDX
pip install cyclonedx-bom
cyclonedx-py -r --format json -o sbom.spdx.json

# Inclure le SBOM dans les releases
# FICHIER: .github/workflows/release.yml
- name: Generate SBOM
  run: |
    pip install pip-audit
    pip-audit --format cyclonedx-json --output sbom.json

- name: Upload SBOM to release
  uses: softprops/action-gh-release@v1
  with:
    files: sbom.json


═══════════════════════════════════════════════════════════════════
ÉTAPE 16: CHECKLIST COMPLÈTE AVANT PUBLICATION
═══════════════════════════════════════════════════════════════════

Cette checklist exhaustive vous assure de ne rien oublier avant la
publication de votre module sur PyPI.

─────────────────────────────────────────────────────────────────
ÉTAPE 16.1: Checklist Code et Tests
─────────────────────────────────────────────────────────────────

[ ] CODE SOURCE
  [ ] Tout le code est dans le dossier mathtools/
  [ ] Chaque sous-dossier a un __init__.py
  [ ] Pas de code commenté ou debug (print, pdb, etc.)
  [ ] Pas de TODO ou FIXME critiques dans le code
  [ ] Pas de chemins absolus hardcodés
  [ ] Pas de secrets ou tokens dans le code
  
  Vérifier:
  grep -r "print(" mathtools/  # Trouver les print() debug
  grep -r "pdb" mathtools/     # Trouver les breakpoints
  grep -r "TODO" mathtools/    # Lister les TODOs
  grep -r "FIXME" mathtools/   # Lister les FIXME

[ ] QUALITÉ DU CODE
  [ ] Code formaté avec Black: black mathtools/
  [ ] Imports triés avec isort: isort mathtools/
  [ ] Pas d'erreurs flake8: flake8 mathtools/
  [ ] Pas d'erreurs mypy: mypy mathtools/
  [ ] Pas d'erreurs pylint: pylint mathtools/
  [ ] Complexité cyclomatique acceptable
  
  Commandes:
  black --check mathtools/
  isort --check mathtools/
  flake8 mathtools/ --max-complexity=10
  mypy mathtools/ --strict

[ ] TESTS
  [ ] Tous les tests passent: pytest
  [ ] Couverture > 80%: pytest --cov=mathtools --cov-report=term
  [ ] Tests sur Python 3.8, 3.9, 3.10, 3.11, 3.12
  [ ] Tests sur Linux, Windows, macOS
  [ ] Pas de tests marqués @pytest.mark.skip sans raison
  [ ] Tous les cas limites testés (0, None, "", négatifs, etc.)
  [ ] Tests de performance pour fonctions critiques
  
  Vérifier couverture:
  pytest --cov=mathtools --cov-report=html
  open htmlcov/index.html  # Identifier code non testé

[ ] VALIDATION ENTRÉES/SORTIES
  [ ] Toutes les fonctions publiques valident leurs entrées
  [ ] Type hints sur toutes les fonctions publiques
  [ ] Gestion d'erreurs appropriée (pas de raise Exception générique)
  [ ] Messages d'erreur clairs et informatifs
  [ ] Docstrings complètes (Args, Returns, Raises, Examples)

[ ] PERFORMANCE
  [ ] Pas de boucles infinies possibles
  [ ] Limites sur tailles d'entrées (prévention DoS)
  [ ] Pas de récursion non bornée
  [ ] Cache pour calculs coûteux si approprié
  [ ] Pas de fuites mémoire


─────────────────────────────────────────────────────────────────
ÉTAPE 16.2: Checklist Documentation
─────────────────────────────────────────────────────────────────

[ ] README.md
  [ ] Titre clair et description concise
  [ ] Badges (build, coverage, version, license)
  [ ] Section Installation (pip install mathtools)
  [ ] Exemples d'utilisation basiques
  [ ] Lien vers documentation complète
  [ ] Section Contribution
  [ ] Section Licence
  [ ] Pas de liens brisés: markdown-link-check README.md
  [ ] Images/GIFs fonctionnent si présents

[ ] CHANGELOG.md
  [ ] Existe et suit le format Keep a Changelog
  [ ] Dernière version documentée avec date
  [ ] Sections: Added, Changed, Deprecated, Removed, Fixed, Security
  [ ] Liens vers commits/PRs pour chaque changement

[ ] LICENSE
  [ ] Fichier LICENSE présent
  [ ] Licence choisie (MIT recommandée)
  [ ] Année et nom d'auteur corrects
  [ ] Correspond au classifier dans pyproject.toml

[ ] CONTRIBUTING.md
  [ ] Guide pour contribuer présent
  [ ] Instructions setup environnement dev
  [ ] Standards de code expliqués
  [ ] Process PR documenté
  [ ] Code de conduite mentionné ou inclus

[ ] SECURITY.md
  [ ] Politique de sécurité présente
  [ ] Process de divulgation expliqué
  [ ] Email de contact sécurité fourni
  [ ] Versions supportées listées

[ ] DOCUMENTATION SPHINX
  [ ] Doc se build sans erreur: cd docs && make html
  [ ] Pas de warnings Sphinx
  [ ] Page d'accueil (index.rst) complète
  [ ] API reference générée automatiquement
  [ ] Exemples fonctionnent et sont testés
  [ ] Tutoriels/guides présents si module complexe
  [ ] Tous les liens internes fonctionnent
  
  Tester localement:
  cd docs
  make clean html
  python -m http.server 8000 -d build/html


─────────────────────────────────────────────────────────────────
ÉTAPE 16.3: Checklist Configuration et Déploiement
─────────────────────────────────────────────────────────────────

[ ] pyproject.toml
  [ ] Nom du package unique et disponible sur PyPI
  [ ] Version cohérente partout (__init__.py, docs/conf.py)
  [ ] Description claire et concise
  [ ] Auteur et email corrects
  [ ] Licence spécifiée
  [ ] URLs (homepage, repository, documentation, issues) valides
  [ ] Classifiers appropriés et complets
  [ ] Python version minimale correcte (requires-python)
  [ ] Dépendances listées avec versions appropriées
  [ ] Optional dependencies configurées ([dev], [docs], etc.)
  [ ] Entry points (scripts CLI) fonctionnels si présents
  
  Valider:
  python -c "import tomli; tomli.load(open('pyproject.toml', 'rb'))"

[ ] FICHIERS OBLIGATOIRES
  [ ] pyproject.toml
  [ ] README.md
  [ ] LICENSE
  [ ] CHANGELOG.md
  [ ] __init__.py dans chaque package
  
[ ] FICHIERS CONFIGURATION
  [ ] .gitignore complet (.venv/, dist/, *.pyc, etc.)
  [ ] .readthedocs.yaml si utilisant Read the Docs
  [ ] .github/workflows/ pour CI/CD
  [ ] .pre-commit-config.yaml
  [ ] .bandit ou configuration dans pyproject.toml

[ ] GIT ET GITHUB
  [ ] Repository initialisé: git init
  [ ] Tous fichiers commités: git status
  [ ] Remote GitHub ajouté et pushé
  [ ] Pas de fichiers sensibles commités (.env, secrets)
  [ ] Branch principale nommée "main"
  [ ] .gitignore inclut tout le nécessaire
  [ ] Tags créés pour versions: git tag v0.1.0
  
  Vérifier:
  git log --oneline
  git remote -v
  git ls-files | grep -E "(\.env|secret|password)"  # Ne doit rien retourner

[ ] GITHUB SETTINGS
  [ ] Description du repository complète
  [ ] Topics/tags ajoutés (python, library, etc.)
  [ ] About section avec URL site web
  [ ] Issues activées
  [ ] Discussions activées si communauté
  [ ] Branch protection sur main (require reviews)
  [ ] Dependabot activé
  [ ] Security advisories activées


─────────────────────────────────────────────────────────────────
ÉTAPE 16.4: Checklist Sécurité
─────────────────────────────────────────────────────────────────

[ ] SCAN DE SÉCURITÉ
  [ ] safety check: pas de vulnérabilités connues
  [ ] bandit -r mathtools/: pas de problèmes High
  [ ] pip-audit: pas de CVEs
  [ ] gitleaks: pas de secrets détectés
  
  Commandes:
  safety check
  bandit -r mathtools/ -ll
  pip-audit
  gitleaks detect

[ ] CODE SÉCURISÉ
  [ ] Pas de eval() ou exec()
  [ ] Pas de pickle avec données non fiables
  [ ] Validation de toutes entrées utilisateur
  [ ] Pas de SQL injection possible
  [ ] Pas de path traversal (../../etc/passwd)
  [ ] Limites sur tailles d'entrées (DoS prevention)
  [ ] Secrets dans variables d'environnement, pas hardcodés
  [ ] Logging ne révèle pas d'infos sensibles

[ ] DÉPENDANCES
  [ ] Versions minimales spécifiées
  [ ] Pas de dépendances obsolètes
  [ ] Pas de dépendances inutilisées
  [ ] Dependabot configuré pour mises à jour auto
  
  Vérifier:
  pip list --outdated
  pipdeptree  # Voir l'arbre des dépendances

[ ] SBOM (Software Bill of Materials)
  [ ] SBOM généré: pip-audit --format cyclonedx-json -o sbom.json
  [ ] Inclus dans releases GitHub


─────────────────────────────────────────────────────────────────
ÉTAPE 16.5: Checklist Qualité et Performance
─────────────────────────────────────────────────────────────────

[ ] COMPATIBILITÉ
  [ ] Testé sur Python 3.8+
  [ ] Testé sur Windows, Linux, macOS
  [ ] Pas de dépendances OS-spécifiques non documentées
  [ ] Chemins utilisant pathlib (cross-platform)
  [ ] Encodage spécifié (utf-8) pour fichiers

[ ] PERFORMANCE
  [ ] Pas de bottlenecks évidents
  [ ] Algorithmes O(n) ou mieux quand possible
  [ ] Cache pour opérations coûteuses
  [ ] Tests de benchmark pour opérations critiques
  
  Profiler:
  python -m cProfile -o profile.stats mathtools/core.py
  python -c "import pstats; p=pstats.Stats('profile.stats'); p.sort_stats('cumulative'); p.print_stats(20)"

[ ] TAILLE DU PACKAGE
  [ ] Pas de fichiers volumineux inutiles
  [ ] Images optimisées si présentes
  [ ] Pas de données de test dans le package final
  
  Vérifier taille:
  python -m build
  ls -lh dist/
  unzip -l dist/*.whl | tail -20  # Voir contenu

[ ] ACCESSIBILITÉ
  [ ] API simple et intuitive
  [ ] Noms de fonctions/variables clairs
  [ ] Pas d'abréviations obscures
  [ ] Exemples fournis pour chaque fonctionnalité
  [ ] Messages d'erreur utiles et actionnables


─────────────────────────────────────────────────────────────────
ÉTAPE 16.6: Validation Finale
─────────────────────────────────────────────────────────────────

[ ] BUILD ET DISTRIBUTION
  [ ] Build se termine sans erreur: python -m build
  [ ] Deux fichiers créés: .tar.gz et .whl
  [ ] twine check passe: twine check dist/*
  [ ] Contenu du wheel correct: unzip -l dist/*.whl
  
  Commandes:
  rm -rf dist/ build/ *.egg-info
  python -m build
  twine check dist/*

[ ] TESTPYPI (TOUJOURS TESTER D'ABORD!)
  [ ] Compte TestPyPI créé
  [ ] Token API généré
  [ ] Upload réussi: twine upload --repository testpypi dist/*
  [ ] Page TestPyPI correcte: https://test.pypi.org/project/mathtools/
  [ ] Installation testée: pip install -i https://test.pypi.org/simple/ mathtools
  [ ] Import fonctionne: python -c "import mathtools"
  [ ] Tests basiques passent dans environnement propre

[ ] READ THE DOCS
  [ ] Compte créé et projet importé
  [ ] .readthedocs.yaml configuré
  [ ] Build réussi sur readthedocs.org
  [ ] Doc accessible: https://mathtools.readthedocs.io
  [ ] Toutes les pages s'affichent correctement
  [ ] Webhook GitHub configuré pour builds automatiques

[ ] CI/CD
  [ ] GitHub Actions configuré
  [ ] Tests automatiques passent sur push
  [ ] Build doc automatique fonctionne
  [ ] Scan sécurité automatique configuré
  [ ] Auto-merge Dependabot configuré (optionnel)

[ ] VÉRIFICATION FINALE
  [ ] Relire README.md une dernière fois
  [ ] Vérifier version cohérente partout
  [ ] Tester dans environnement complètement propre
  [ ] Demander revue code à un pair si possible
  [ ] Vérifier compatibilité avec versions Python cibles
  
  Test installation propre:
  mkdir /tmp/test-mathtools
  cd /tmp/test-mathtools
  python -m venv .venv
  source .venv/bin/activate
  pip install -i https://test.pypi.org/simple/ mathtools
  python -c "from mathtools import *; print(factorial(5))"

[ ] PRÊT POUR PUBLICATION!
  [ ] Toutes les cases cochées ci-dessus
  [ ] Confiant dans la qualité du code
  [ ] Documentation complète et claire
  [ ] Tests exhaustifs et passants
  [ ] Sécurité vérifiée
  [ ] Testé sur TestPyPI avec succès
  
  [OK] GO POUR PUBLICATION SUR PYPI!


Parfait ! Je vais t’expliquer **comment écrire ton propre module C pour Python** de manière claire et progressive, avec un exemple complet.
Tu verras que ce n’est pas si compliqué, et c’est exactement comme NumPy fonctionne sous le capot pour ses opérations rapides.

---

# * 1. Principe de base

Un **module C pour Python** te permet :

* d’écrire des fonctions en **C rapide**
* de les appeler depuis Python comme n’importe quelle fonction Python

**Exemple :** tu écris une fonction C `add(int a, int b)` -> tu peux l’utiliser en Python comme `add(2,3)`.

---

# [BLEU] 2. Étape 1 : Créer un fichier C

Créons un fichier `mymodule.c` :

```c
#include <Python.h>  // Obligatoire pour interfacer avec Python

// Une fonction C que Python pourra appeler
static PyObject* add(PyObject* self, PyObject* args) {
    int a, b;
    
    // Extraire les arguments passés depuis Python
    if (!PyArg_ParseTuple(args, "ii", &a, &b)) {
        return NULL;  // Erreur si mauvais arguments
    }

    int result = a + b;

    // Retourner le résultat à Python
    return PyLong_FromLong(result);
}

// Définir les méthodes du module
static PyMethodDef MyMethods[] = {
    {"add", add, METH_VARARGS, "Additionne deux nombres"},
    {NULL, NULL, 0, NULL}  // fin de la table
};

// Définir le module
static struct PyModuleDef mymodule = {
    PyModuleDef_HEAD_INIT,
    "mymodule",          // nom du module
    "Un exemple de module C", // doc
    -1,
    MyMethods
};

// Fonction d’initialisation
PyMODINIT_FUNC PyInit_mymodule(void) {
    return PyModule_Create(&mymodule);
}
```

[OK] Ce fichier contient tout ce qu’il faut pour un module C Python simple.

---

# [BLEU] 3. Étape 2 : Créer un `setup.py` pour compiler le module

On utilise **setuptools** pour créer le module compilé.

```python
from setuptools import setup, Extension

module = Extension(
    'mymodule',         # nom du module
    sources=['mymodule.c'],  # fichiers sources C
)

setup(
    name='mymodule',
    version='1.0',
    description='Exemple de module C pour Python',
    ext_modules=[module],
)
```

---

# [BLEU] 4. Étape 3 : Compiler le module

Dans ton terminal, tape :

```bash
python setup.py build
python setup.py install
```

[OK] Python va compiler le C en **fichier partagé** (`.so` sur Linux, `.pyd` sur Windows) et l’installer.

---

# [BLEU] 5. Étape 4 : Utiliser ton module en Python

```python
import mymodule

result = mymodule.add(10, 20)
print(result)  # 30
```

[BRAVO] Et voilà, ton module C fonctionne !

---

# [BLEU] 6. Étape 5 (optionnelle) : Faire plus complexe

Tu peux :

* Retourner des **float** -> `PyFloat_FromDouble`
* Retourner des **listes** -> `PyList_New` + `PyList_SetItem`
* Créer des **arrays rapides** -> utiliser `PyArrayObject` si tu inclus NumPy
* Passer des **tableaux C** -> idéal pour des calculs vectoriels rapides comme NumPy

---

# [BLEU] 7. Pourquoi c’est rapide ?

* Tu écris du **C natif**
* Python ne fait presque pas de travail pour la fonction
* Les boucles et calculs lourds sont **exécutés directement par le CPU**
* Tu peux éviter les objets Python et allocations dynamiques

---

# [BLEU] 8. Résumé

1. Écrire un fichier C avec `Python.h` et `PyObject*`
2. Définir les fonctions et la table de méthodes (`PyMethodDef`)
3. Définir le module (`PyModuleDef`) et la fonction d’initialisation
4. Compiler avec `setuptools`
5. Importer et utiliser le module en Python

-> Cette méthode est **la base de NumPy, SciPy, pandas et beaucoup d’autres modules** qui ont besoin de vitesse.



═══════════════════════════════════════════════════════════════════
ÉTAPE 17: COMMANDES RAPIDES - AIDE-MÉMOIRE
═══════════════════════════════════════════════════════════════════

Ce guide de référence rapide liste toutes les commandes essentielles.

─────────────────────────────────────────────────────────────────
ÉTAPE 17.1: Commandes de développement quotidien
─────────────────────────────────────────────────────────────────

# ═══════════════════════════════════════════════════════════════
# ENVIRONNEMENT VIRTUEL
# ═══════════════════════════════════════════════════════════════

# Créer environnement
python -m venv .venv
python3 -m venv .venv  # Sur certains systèmes Linux

# Activer
source .venv/bin/activate              # Linux/Mac
.venv\Scripts\activate                 # Windows CMD
.venv\Scripts\Activate.ps1             # Windows PowerShell

# Désactiver
deactivate

# Supprimer
rm -rf .venv                           # Linux/Mac
rmdir /s .venv                         # Windows

# ═══════════════════════════════════════════════════════════════
# INSTALLATION
# ═══════════════════════════════════════════════════════════════

# Installer en mode développement
pip install -e .
pip install -e ".[dev]"                # Avec dépendances dev
pip install -e ".[dev,docs]"           # Multiple extras

# Installer depuis requirements
pip install -r requirements.txt
pip install -r requirements-dev.txt

# Mettre à jour pip
python -m pip install --upgrade pip

# Lister packages installés
pip list
pip list --outdated                    # Packages obsolètes
pip freeze > requirements.txt          # Exporter

# Désinstaller
pip uninstall mathtools
pip uninstall -r requirements.txt -y   # Tout désinstaller

# ═══════════════════════════════════════════════════════════════
# GESTION DES DÉPENDANCES
# ═══════════════════════════════════════════════════════════════

# Voir l'arbre des dépendances
pip install pipdeptree
pipdeptree
pipdeptree --reverse                   # Qui dépend de quoi

# Vérifier compatibilité
pip check

# Rechercher un package
pip search requests  # (désactivé sur PyPI actuellement)


─────────────────────────────────────────────────────────────────
ÉTAPE 17.2: Commandes de test et qualité
─────────────────────────────────────────────────────────────────

# ═══════════════════════════════════════════════════════════════
# TESTS PYTEST
# ═══════════════════════════════════════════════════════════════

# Lancer tous les tests
pytest

# Tests avec options utiles
pytest -v                              # Verbose
pytest -x                              # Stop au premier échec
pytest -s                              # Afficher print()
pytest --tb=short                      # Traceback court
pytest -k "test_name"                  # Tests matchant nom
pytest tests/test_core.py              # Fichier spécifique
pytest tests/test_core.py::test_func   # Test spécifique

# Tests parallèles (plus rapide)
pip install pytest-xdist
pytest -n auto                         # Auto CPUs
pytest -n 4                            # 4 processus

# Couverture de code
pytest --cov=mathtools                 # Couverture basique
pytest --cov=mathtools --cov-report=html    # Rapport HTML
pytest --cov=mathtools --cov-report=term-missing  # Lignes manquantes

# Voir rapport HTML
open htmlcov/index.html                # Mac/Linux
start htmlcov/index.html               # Windows

# Markers
pytest -m slow                         # Tests marqués slow
pytest -m "not slow"                   # Exclure slow

# ═══════════════════════════════════════════════════════════════
# FORMATAGE ET LINTING
# ═══════════════════════════════════════════════════════════════

# Black (formatage)
black mathtools/                       # Formater
black --check mathtools/               # Vérifier seulement
black --diff mathtools/                # Voir changements

# isort (tri imports)
isort mathtools/
isort --check mathtools/
isort --diff mathtools/

# Formater tout
black . && isort .

# flake8 (linting)
flake8 mathtools/
flake8 mathtools/ --max-line-length=88
flake8 mathtools/ --statistics         # Stats
flake8 mathtools/ --count              # Nombre erreurs

# pylint (linting strict)
pylint mathtools/
pylint mathtools/ --disable=C0111      # Ignorer certaines règles

# mypy (vérification types)
mypy mathtools/
mypy mathtools/ --strict               # Mode strict
mypy mathtools/ --ignore-missing-imports

# ═══════════════════════════════════════════════════════════════
# PRE-COMMIT
# ═══════════════════════════════════════════════════════════════

# Installer hooks
pre-commit install

# Lancer manuellement
pre-commit run --all-files
pre-commit run black --all-files       # Hook spécifique

# Mettre à jour hooks
pre-commit autoupdate

# Désinstaller
pre-commit uninstall


─────────────────────────────────────────────────────────────────
ÉTAPE 17.3: Commandes de documentation
─────────────────────────────────────────────────────────────────

# ═══════════════════════════════════════════════════════════════
# SPHINX
# ═══════════════════════════════════════════════════════════════

# Initialiser Sphinx (première fois)
cd docs
sphinx-quickstart

# Build HTML
cd docs
make html                              # Linux/Mac
make.bat html                          # Windows

# Build autres formats
make latex                             # LaTeX/PDF
make epub                              # ePub
make man                               # Pages man

# Nettoyer
make clean

# Build automatique sur changement
pip install sphinx-autobuild
sphinx-autobuild source build/html     # Auto-reload

# Servir localement
python -m http.server 8000 -d build/html

# Vérifier liens
make linkcheck

# ═══════════════════════════════════════════════════════════════
# DOCUMENTATION API
# ═══════════════════════════════════════════════════════════════

# Générer stubs API avec sphinx-apidoc
cd docs
sphinx-apidoc -o source/ ../mathtools/

# Inclure dans index.rst:
# .. automodule:: mathtools
#    :members:


─────────────────────────────────────────────────────────────────
ÉTAPE 17.4: Commandes de versionnement et Git
─────────────────────────────────────────────────────────────────

# ═══════════════════════════════════════════════════════════════
# GIT BASIQUE
# ═══════════════════════════════════════════════════════════════

# Initialiser
git init
git remote add origin https://github.com/user/repo.git

# Status et diff
git status
git diff
git diff --staged

# Add et commit
git add .
git add -p                             # Add interactif
git commit -m "Message"
git commit --amend                     # Modifier dernier commit

# Push et pull
git push
git push origin main
git pull

# Branches
git branch                             # Lister
git branch feature-x                   # Créer
git checkout feature-x                 # Changer
git checkout -b feature-x              # Créer et changer
git merge feature-x                    # Merger
git branch -d feature-x                # Supprimer

# Tags
git tag v0.1.0                         # Créer tag
git tag -a v0.1.0 -m "Version 0.1.0"  # Tag annoté
git push --tags                        # Pousser tags
git tag -l                             # Lister tags

# Historique
git log
git log --oneline
git log --graph --oneline --all

# ═══════════════════════════════════════════════════════════════
# BUMP2VERSION
# ═══════════════════════════════════════════════════════════════

# Installer
pip install bump2version

# Incrémenter version
bump2version patch                     # 0.1.0 -> 0.1.1
bump2version minor                     # 0.1.1 -> 0.2.0
bump2version major                     # 0.2.0 -> 1.0.0

# Options
bump2version --dry-run patch           # Simulation
bump2version --allow-dirty patch       # Autoriser modifs non commitées
bump2version --no-commit patch         # Ne pas commiter
bump2version --no-tag patch            # Ne pas créer tag


─────────────────────────────────────────────────────────────────
ÉTAPE 17.5: Commandes de publication
─────────────────────────────────────────────────────────────────

# ═══════════════════════════════════════════════════════════════
# BUILD
# ═══════════════════════════════════════════════════════════════

# Installer outils
pip install build twine

# Nettoyer builds précédents
rm -rf dist/ build/ *.egg-info         # Linux/Mac
rmdir /s dist build & del /s *.egg-info  # Windows

# Build
python -m build

# Vérifier contenu
ls -lh dist/                           # Taille fichiers
unzip -l dist/*.whl                    # Contenu wheel
tar -tzf dist/*.tar.gz                 # Contenu source

# ═══════════════════════════════════════════════════════════════
# VALIDATION
# ═══════════════════════════════════════════════════════════════

# Vérifier packages
twine check dist/*

# ═══════════════════════════════════════════════════════════════
# PUBLICATION
# ═══════════════════════════════════════════════════════════════

# TestPyPI (TOUJOURS TESTER D'ABORD!)
twine upload --repository testpypi dist/*

# PyPI (production)
twine upload dist/*

# Avec token spécifique
twine upload --repository-url https://upload.pypi.org/legacy/ \
  --username __token__ \
  --password pypi-YOUR_TOKEN \
  dist/*

# Installation depuis TestPyPI
pip install --index-url https://test.pypi.org/simple/ mathtools

# Installation avec dépendances depuis les deux sources
pip install --index-url https://test.pypi.org/simple/ \
  --extra-index-url https://pypi.org/simple/ \
  mathtools


─────────────────────────────────────────────────────────────────
ÉTAPE 17.6: Commandes de diagnostic et debug
─────────────────────────────────────────────────────────────────

# ═══════════════════════════════════════════════════════════════
# INFORMATIONS SYSTÈME
# ═══════════════════════════════════════════════════════════════

# Version Python
python --version
python3 --version

# Localisation Python
which python                           # Linux/Mac
where python                           # Windows

# Info détaillée
python -c "import sys; print(sys.version)"
python -c "import platform; print(platform.platform())"

# Variables d'environnement
echo $PATH                             # Linux/Mac
echo %PATH%                            # Windows
printenv                               # Toutes les variables

# ═══════════════════════════════════════════════════════════════
# DEBUG PACKAGE
# ═══════════════════════════════════════════════════════════════

# Vérifier import
python -c "import mathtools; print(mathtools.__version__)"

# Localisation package
python -c "import mathtools; print(mathtools.__file__)"

# Lister contenu
python -c "import mathtools; print(dir(mathtools))"

# Info package
pip show mathtools

# Dépendances
pipdeptree -p mathtools

# ═══════════════════════════════════════════════════════════════
# PROFILING PERFORMANCE
# ═══════════════════════════════════════════════════════════════

# cProfile
python -m cProfile script.py
python -m cProfile -o output.stats script.py

# Analyser stats
python -c "import pstats; p=pstats.Stats('output.stats'); p.sort_stats('cumulative'); p.print_stats(20)"

# line_profiler (ligne par ligne)
pip install line_profiler
kernprof -l -v script.py

# memory_profiler
pip install memory_profiler
python -m memory_profiler script.py

# ═══════════════════════════════════════════════════════════════
# SÉCURITÉ
# ═══════════════════════════════════════════════════════════════

# Scanner vulnérabilités
safety check
pip-audit
bandit -r mathtools/

# Scanner secrets
gitleaks detect
gitleaks protect --staged              # Pre-commit

# SBOM
pip-audit --format cyclonedx-json -o sbom.json


═══════════════════════════════════════════════════════════════════
ÉTAPE 18: RESSOURCES ET LIENS UTILES
═══════════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────────────────
ÉTAPE 18.1: Documentation officielle Python
─────────────────────────────────────────────────────────────────

[DOCS] PACKAGING
• Python Packaging User Guide
  https://packaging.python.org
  Guide officiel complet sur le packaging Python

• PyPI - Python Package Index
  https://pypi.org
  Repository principal des packages Python

• TestPyPI
  https://test.pypi.org
  Serveur de test pour PyPI

• Packaging Guide Tutorials
  https://packaging.python.org/tutorials/
  Tutoriels étape par étape

• PEP 517 - Build System
  https://peps.python.org/pep-0517/
  Spécification du système de build moderne

• PEP 518 - pyproject.toml
  https://peps.python.org/pep-0518/
  Spécification de pyproject.toml

[DOCS] DOCUMENTATION
• Sphinx Documentation
  https://www.sphinx-doc.org
  Générateur de documentation Python

• Read the Docs
  https://docs.readthedocs.io
  Hébergement gratuit de documentation

• reStructuredText Primer
  https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
  Guide du format reST

• Google Style Docstrings
  https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html
  Format de docstrings recommandé

[DOCS] STANDARDS
• PEP 8 - Style Guide
  https://peps.python.org/pep-0008/
  Guide officiel du style Python

• Semantic Versioning
  https://semver.org
  Standard de versionnement

• Keep a Changelog
  https://keepachangelog.com
  Format standardisé pour CHANGELOG

• Choose a License
  https://choosealicense.com
  Aide au choix de licence


─────────────────────────────────────────────────────────────────
ÉTAPE 18.2: Outils de développement
─────────────────────────────────────────────────────────────────

[OUTILS] BUILD ET DISTRIBUTION
• build
  https://pypa-build.readthedocs.io
  Outil moderne de build

• twine
  https://twine.readthedocs.io
  Upload sécurisé vers PyPI

• setuptools
  https://setuptools.pypa.io
  Outil de packaging standard

• wheel
  https://wheel.readthedocs.io
  Format de distribution binaire

[OUTILS] TESTS
• pytest
  https://docs.pytest.org
  Framework de test #1

• pytest-cov
  https://pytest-cov.readthedocs.io
  Plugin coverage pour pytest

• coverage.py
  https://coverage.readthedocs.io
  Mesure de couverture de code

• tox
  https://tox.readthedocs.io
  Test sur plusieurs environnements

• hypothesis
  https://hypothesis.readthedocs.io
  Property-based testing

[OUTILS] QUALITÉ DE CODE
• Black
  https://black.readthedocs.io
  Formatteur Python "sans compromis"

• isort
  https://pycqa.github.io/isort/
  Tri automatique des imports

• flake8
  https://flake8.pycqa.org
  Linter Python

• pylint
  https://pylint.pycqa.org
  Linter Python (plus strict)

• mypy
  https://mypy.readthedocs.io
  Vérification de types statiques

• pre-commit
  https://pre-commit.com
  Framework de hooks Git

[OUTILS] SÉCURITÉ
• Safety
  https://pyup.io/safety/
  Scanner de vulnérabilités

• Bandit
  https://bandit.readthedocs.io
  Linter de sécurité

• pip-audit
  https://pypi.org/project/pip-audit/
  Audit de sécurité moderne

• gitleaks
  https://github.com/gitleaks/gitleaks
  Scanner de secrets Git

• Trivy
  https://github.com/aquasecurity/trivy
  Scanner de vulnérabilités multi-usage

[OUTILS] VERSIONING
• bump2version
  https://github.com/c4urself/bump2version
  Gestion automatique des versions

• semantic-release
  https://python-semantic-release.readthedocs.io
  Release automatisée

[OUTILS] AUTRES
• pipdeptree
  https://github.com/tox-dev/pipdeptree
  Visualisation des dépendances

• pyenv
  https://github.com/pyenv/pyenv
  Gestion de versions Python

• poetry
  https://python-poetry.org
  Alternative moderne pour packaging


─────────────────────────────────────────────────────────────────
ÉTAPE 18.3: Plateformes et services
─────────────────────────────────────────────────────────────────

[CLOUD] HÉBERGEMENT CODE
• GitHub
  https://github.com
  Plateforme #1 pour open source

• GitLab
  https://gitlab.com
  Alternative avec CI/CD intégré

• Bitbucket
  https://bitbucket.org
  Plateforme Atlassian

[CLOUD] CI/CD
• GitHub Actions
  https://github.com/features/actions
  CI/CD intégré à GitHub

• Travis CI
  https://travis-ci.org
  CI/CD populaire (gratuit pour open source)

• CircleCI
  https://circleci.com
  CI/CD moderne

• Azure Pipelines
  https://azure.microsoft.com/en-us/services/devops/pipelines/
  CI/CD Microsoft (gratuit pour open source)

[CLOUD] QUALITÉ ET MONITORING
• Codecov
  https://codecov.io
  Visualisation de couverture de code

• Coveralls
  https://coveralls.io
  Alternative à Codecov

• Snyk
  https://snyk.io
  Monitoring de sécurité

• Sonar Cloud
  https://sonarcloud.io
  Analyse de qualité de code

[CLOUD] DOCUMENTATION
• Read the Docs
  https://readthedocs.org
  Hébergement documentation (#1)

• GitHub Pages
  https://pages.github.com
  Hébergement statique GitHub

• GitBook
  https://www.gitbook.com
  Plateforme de documentation


─────────────────────────────────────────────────────────────────
ÉTAPE 18.4: Templates et exemples
─────────────────────────────────────────────────────────────────

[LISTE] TEMPLATES
• PyPA Sample Project
  https://github.com/pypa/sampleproject
  Template officiel PyPA

• Python Project Template
  https://github.com/rochacbruno/python-project-template
  Template moderne complet

• Cookiecutter PyPackage
  https://github.com/audreyfeldroy/cookiecutter-pypackage
  Générateur de projet avec Cookiecutter

• Poetry Template
  https://github.com/fpgmaas/cookiecutter-poetry
  Template pour Poetry

[OBJECTIF] EXEMPLES DE MODULES BIEN FAITS
• requests
  https://github.com/psf/requests
  Client HTTP élégant

• click
  https://github.com/pallets/click
  Framework CLI

• rich
  https://github.com/Textualize/rich
  Affichage terminal enrichi

• pytest
  https://github.com/pytest-dev/pytest
  Framework de test de référence


─────────────────────────────────────────────────────────────────
ÉTAPE 18.5: Communauté et support
─────────────────────────────────────────────────────────────────

[SPEECH_BALLOON] COMMUNAUTÉS
• Python Discord
  https://discord.gg/python
  Serveur Discord Python officiel

• r/Python
  https://reddit.com/r/Python
  Subreddit Python

• r/learnpython
  https://reddit.com/r/learnpython
  Pour débutants

• Stack Overflow
  https://stackoverflow.com/questions/tagged/python
  Questions & Réponses

• Python Forum
  https://python-forum.io
  Forum officiel Python

• Real Python Community
  https://realpython.com/community/
  Communauté Real Python

[NEWSPAPER] ACTUALITÉS
• Python Weekly
  https://www.pythonweekly.com
  Newsletter hebdomadaire

• PyCoder's Weekly
  https://pycoders.com
  Newsletter code Python

• Planet Python
  https://planetpython.org
  Agrégateur de blogs Python

• Python.org Blog
  https://blog.python.org
  Blog officiel


─────────────────────────────────────────────────────────────────
ÉTAPE 18.6: Badges et indicateurs
─────────────────────────────────────────────────────────────────

[LABEL] GÉNÉRATEUR DE BADGES
• Shields.io
  https://shields.io
  Générateur universel de badges

• Badge Fury
  https://badge.fury.io
  Badges PyPI

[GRAPHIQUE] BADGES POPULAIRES
```markdown
<!-- Version PyPI -->
[![PyPI version](https://badge.fury.io/py/mathtools.svg)](https://pypi.org/project/mathtools/)

<!-- Python versions -->
[![Python](https://img.shields.io/pypi/pyversions/mathtools.svg)](https://pypi.org/project/mathtools/)

<!-- Build status -->
[![Build](https://github.com/user/mathtools/workflows/Tests/badge.svg)](https://github.com/user/mathtools/actions)

<!-- Coverage -->
[![Coverage](https://codecov.io/gh/user/mathtools/branch/main/graph/badge.svg)](https://codecov.io/gh/user/mathtools)

<!-- Documentation -->
[![Docs](https://readthedocs.org/projects/mathtools/badge/)](https://mathtools.readthedocs.io)

<!-- License -->
[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

<!-- Downloads -->
[![Downloads](https://pepy.tech/badge/mathtools)](https://pepy.tech/project/mathtools)

<!-- Code style -->
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

<!-- Security -->
[![Security](https://snyk.io/test/github/user/mathtools/badge.svg)](https://snyk.io/test/github/user/mathtools)
```


─────────────────────────────────────────────────────────────────
ÉTAPE 18.7: Livres et tutoriels recommandés
─────────────────────────────────────────────────────────────────

[GUIDE] LIVRES
• "Python Packaging: Creating Packages" (Real Python)
  https://realpython.com/python-modules-packages/

• "Publishing Python Packages" by Dane Hillard
  https://www.manning.com/books/publishing-python-packages

• "Effective Python" by Brett Slatkin
  90 Specific Ways to Write Better Python

• "The Hitchhiker's Guide to Python"
  https://docs.python-guide.org
  Livre gratuit en ligne

[COURS] TUTORIELS EN LIGNE
• Real Python
  https://realpython.com
  Tutoriels Python de qualité

• Python.org Tutorials
  https://docs.python.org/3/tutorial/
  Tutoriel officiel

• Full Stack Python
  https://www.fullstackpython.com
  Guide complet développement Python

• TestDriven.io
  https://testdriven.io
  TDD et bonnes pratiques

[MOVIE_CAMERA] VIDÉOS
• Talk Python Podcast
  https://talkpython.fm
  Podcast Python populaire

• Python Bytes
  https://pythonbytes.fm
  Actualités Python hebdomadaires

• PyVideos
  https://pyvideo.org
  Conférences Python archivées


═══════════════════════════════════════════════════════════════════
ÉTAPE 19: GUIDE DE DÉPANNAGE COMPLET
═══════════════════════════════════════════════════════════════════

Ce guide recense tous les problèmes courants et leurs solutions.

─────────────────────────────────────────────────────────────────
ÉTAPE 19.1: Problèmes d'installation et configuration
─────────────────────────────────────────────────────────────────

[X] PROBLÈME: "pip: command not found"
─────────────────────────────────────────────────────────────────
CAUSE: pip n'est pas installé ou pas dans le PATH

SOLUTIONS:
# Python 3.4+ inclut pip, réinstaller Python
# Ou installer pip manuellement:
python -m ensurepip --upgrade
# Ou:
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py

# Vérifier:
python -m pip --version


[X] PROBLÈME: "ModuleNotFoundError: No module named 'mathtools'"
─────────────────────────────────────────────────────────────────
CAUSES POSSIBLES:
1. Package pas installé
2. Mauvais environnement virtuel activé
3. Nom de package incorrect
4. __init__.py manquant

DIAGNOSTIC:
# Vérifier que le package est installé
pip list | grep mathtools
pip show mathtools

# Vérifier quel Python est utilisé
which python
python -c "import sys; print(sys.executable)"

# Vérifier le PYTHONPATH
python -c "import sys; print('\n'.join(sys.path))"

SOLUTIONS:
# Installer le package
pip install mathtools

# S'assurer du bon environnement
which pip  # Doit pointer vers .venv/bin/pip
source .venv/bin/activate

# Réinstaller en mode dev
pip install -e .

# Vérifier __init__.py présent
ls -la mathtools/__init__.py


[X] PROBLÈME: "ERROR: Could not build wheels for X"
─────────────────────────────────────────────────────────────────
CAUSE: Dépendance nécessite compilation (C extension)

SOLUTIONS:
# Installer outils de build

# Ubuntu/Debian:
sudo apt-get install python3-dev build-essential

# CentOS/RHEL:
sudo yum install python3-devel gcc

# macOS:
xcode-select --install

# Windows: Installer Visual Studio Build Tools
# https://visualstudio.microsoft.com/downloads/


[X] PROBLÈME: "Permission denied" lors de pip install
─────────────────────────────────────────────────────────────────
CAUSE: Installation système sans sudo

SOLUTIONS:
# [X] NE JAMAIS FAIRE: sudo pip install
# [OK] Utiliser environnement virtuel:
python -m venv .venv
source .venv/bin/activate
pip install mathtools

# Ou installer en user mode:
pip install --user mathtools


[X] PROBLÈME: Conflit de versions de dépendances
─────────────────────────────────────────────────────────────────
SYMPTÔME:
"""
ERROR: package-a 1.0 requires package-b<2.0, but you have package-b 2.5
"""

DIAGNOSTIC:
pipdeptree  # Voir l'arbre complet

SOLUTIONS:
# Option 1: Recréer environnement propre
deactivate
rm -rf .venv
python -m venv .venv
source .venv/bin/activate
pip install -e .

# Option 2: Résoudre manuellement
pip install 'package-b<2.0'

# Option 3: Utiliser pip-tools
pip install pip-tools
pip-compile requirements.in
pip-sync


─────────────────────────────────────────────────────────────────
ÉTAPE 19.2: Problèmes de publication PyPI
─────────────────────────────────────────────────────────────────

[X] PROBLÈME: "403 Forbidden" lors de twine upload
─────────────────────────────────────────────────────────────────
CAUSES POSSIBLES:
1. Token PyPI invalide/expiré
2. Nom de package déjà pris
3. Mauvais repository URL
4. Pas de permission sur le package existant

DIAGNOSTIC:
# Vérifier token
cat ~/.pypirc
# Token doit commencer par pypi-

# Vérifier si nom existe
curl https://pypi.org/project/mathtools/
# 404 = disponible, 200 = déjà pris

SOLUTIONS:
# Générer nouveau token sur pypi.org
# Account Settings -> Add API token

# Upload avec token explicite:
twine upload \
  --username __token__ \
  --password pypi-YOUR_NEW_TOKEN_HERE \
  dist/*

# Ou changer nom du package dans pyproject.toml


[X] PROBLÈME: "File already exists" sur PyPI
─────────────────────────────────────────────────────────────────
CAUSE: Vous ne pouvez pas ré-uploader la même version

SOLUTION:
# Incrémenter la version
bump2version patch
python -m build
twine upload dist/*

# NOTE: Les fichiers déjà uploadés ne peuvent PAS être supprimés!


[X] PROBLÈME: twine check échoue
─────────────────────────────────────────────────────────────────
SYMPTÔME:
"""
Checking dist/mathtools-0.1.0.tar.gz: FAILED
  `long_description` has syntax errors in markup
"""

CAUSES:
1. README.md mal formaté
2. Images/liens relatifs cassés
3. Markdown non supporté

DIAGNOSTIC:
# Tester rendu localement
pip install readme-renderer
python -m readme_renderer README.md -o /tmp/preview.html
open /tmp/preview.html

SOLUTIONS:
# Vérifier syntaxe Markdown
# Liens doivent être absolus pour PyPI
# Images doivent être en URL complètes

# Dans pyproject.toml:
[project]
readme = {file = "README.md", content-type = "text/markdown"}


[X] PROBLÈME: Package vide ou incomplet
─────────────────────────────────────────────────────────────────
SYMPTÔME: Après installation, import échoue ou fichiers manquants

DIAGNOSTIC:
# Vérifier contenu du wheel
unzip -l dist/*.whl

SOLUTIONS:
# Vérifier pyproject.toml
[tool.setuptools]
packages = ["mathtools"]

# Ou utiliser find automatique:
[tool.setuptools.packages.find]
where = ["."]
include = ["mathtools*"]
exclude = ["tests*"]

# Inclure fichiers data:
[tool.setuptools.package-data]
mathtools = ["data/*.json", "*.txt"]


─────────────────────────────────────────────────────────────────
ÉTAPE 19.3: Problèmes de documentation
─────────────────────────────────────────────────────────────────

[X] PROBLÈME: Sphinx build échoue avec "module not found"
─────────────────────────────────────────────────────────────────
CAUSE: Sphinx ne trouve pas votre module

SOLUTIONS:
# Dans docs/source/conf.py, ajouter:
import os
import sys
sys.path.insert(0, os.path.abspath('../..'))

# Ou installer le module:
cd ../..  # Racine projet
pip install -e .


[X] PROBLÈME: Read the Docs build échoue
─────────────────────────────────────────────────────────────────
DIAGNOSTIC:
# Consulter logs sur readthedocs.org:
# Votre projet -> Builds -> Voir le build échoué

CAUSES COMMUNES:
1. .readthedocs.yaml mal configuré
2. Dépendances manquantes
3. Version Python incorrecte
4. Erreurs Sphinx

SOLUTIONS:
# Fichier .readthedocs.yaml correct:
version: 2
build:
  os: ubuntu-22.04
  tools:
    python: "3.11"
sphinx:
  configuration: docs/source/conf.py
python:
  install:
    - method: pip
      path: .
      extra_requirements:
        - docs

# Tester localement:
cd docs
make clean html
# Corriger toutes les erreurs avant de pusher


[X] PROBLÈME: Docstrings ne s'affichent pas
─────────────────────────────────────────────────────────────────
CAUSE: Extension autodoc pas configurée

SOLUTION:
# Dans docs/source/conf.py:
extensions = [
    'sphinx.ext.autodoc',
    'sphinx.ext.napoleon',  # Pour Google/NumPy style
]

# Dans fichier .rst:
.. automodule:: mathtools
   :members:
   :undoc-members:
   :show-inheritance:


─────────────────────────────────────────────────────────────────
ÉTAPE 19.4: Problèmes de tests et CI/CD
─────────────────────────────────────────────────────────────────

[X] PROBLÈME: Tests passent localement mais échouent sur CI
─────────────────────────────────────────────────────────────────
CAUSES POSSIBLES:
1. Différences OS (Windows vs Linux)
2. Dépendances manquantes sur CI
3. Fichiers locaux non versionnés
4. Variables d'environnement manquantes
5. Timezone différente

SOLUTIONS:
# Utiliser pathlib pour chemins (cross-platform)
from pathlib import Path
path = Path("data") / "file.txt"  # [OK]
# Pas: path = "data\\file.txt"  # [X]

# S'assurer que dépendances sont dans requirements
pip freeze > requirements.txt

# Vérifier fichiers Git
git ls-files tests/
# Tous fichiers de test doivent être listés

# Tester avec tox (simule CI)
pip install tox
tox

# Variables d'env dans GitHub Actions:
env:
  TEST_ENV_VAR: value


[X] PROBLÈME: GitHub Actions échoue avec "permission denied"
─────────────────────────────────────────────────────────────────
CAUSE: Permissions insuffisantes

SOLUTION:
# Dans .github/workflows/xxx.yml:
permissions:
  contents: write      # Pour push
  pull-requests: write # Pour commenter PRs
  issues: write        # Pour créer issues


[X] PROBLÈME: Tests très lents sur CI
─────────────────────────────────────────────────────────────────
SOLUTIONS:
# Paralléliser avec pytest-xdist
pip install pytest-xdist
pytest -n auto

# Caching des dépendances (GitHub Actions):
- name: Cache dependencies
  uses: actions/cache@v3
  with:
    path: ~/.cache/pip
    key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}


─────────────────────────────────────────────────────────────────
ÉTAPE 19.5: Problèmes de performance
─────────────────────────────────────────────────────────────────

[X] PROBLÈME: Import lent du module
─────────────────────────────────────────────────────────────────
DIAGNOSTIC:
python -X importtime -c "import mathtools" 2>&1 | grep mathtools

SOLUTIONS:
# Imports paresseux dans __init__.py
def __getattr__(name):
    if name == "heavy_module":
        from . import heavy_module
        return heavy_module
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

# Ou diviser en sous-modules


[X] PROBLÈME: Fonction très lente
─────────────────────────────────────────────────────────────────
DIAGNOSTIC:
# Profiler avec cProfile
python -m cProfile -o profile.stats script.py
python -c "import pstats; p=pstats.Stats('profile.stats'); p.sort_stats('cumulative'); p.print_stats(20)"

# Profiler ligne par ligne
pip install line_profiler
@profile  # Décorer la fonction
def slow_function():
    pass

kernprof -l -v script.py

SOLUTIONS COMMUNES:
# 1. Utiliser cache
from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_function(n):
    pass

# 2. Utiliser numpy pour calculs
import numpy as np
result = np.sum(array)  # Plus rapide que sum()

# 3. List comprehension au lieu de loops
# [OK] Rapide
result = [x**2 for x in range(1000)]
# [X] Lent
result = []
for x in range(1000):
    result.append(x**2)


─────────────────────────────────────────────────────────────────
ÉTAPE 19.6: Problèmes de compatibilité
─────────────────────────────────────────────────────────────────

[X] PROBLÈME: Module ne fonctionne pas sur Python 3.8
─────────────────────────────────────────────────────────────────
CAUSE: Syntaxe/fonctionnalités Python 3.9+

EXEMPLES ET SOLUTIONS:
# [X] Python 3.9+ seulement
def process(data: dict[str, int]):  # Type hints avec dict
    pass

# [OK] Compatible 3.8+
from typing import Dict
def process(data: Dict[str, int]):
    pass

# [X] Python 3.10+ seulement
match value:
    case 1:
        return "one"

# [OK] Compatible 3.8+
if value == 1:
    return "one"

# Tester sur toutes versions:
# .github/workflows/tests.yml
strategy:
  matrix:
    python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']


[X] PROBLÈME: Module échoue sur Windows
─────────────────────────────────────────────────────────────────
CAUSES COMMUNES:
1. Chemins hardcodés avec /
2. Commandes Unix (rm, ls)
3. Permissions fichiers

SOLUTIONS:
# Utiliser pathlib
from pathlib import Path
path = Path("data") / "file.txt"  # [OK] Cross-platform

# Vérifier OS
import platform
if platform.system() == "Windows":
    # Code Windows-spécifique
    pass

# Utiliser shutil au lieu de commandes OS
import shutil
shutil.rmtree("dir")  # Au lieu de os.system("rm -rf dir")


─────────────────────────────────────────────────────────────────
ÉTAPE 19.7: Problèmes de dépendances
─────────────────────────────────────────────────────────────────

[X] PROBLÈME: "No matching distribution found"
─────────────────────────────────────────────────────────────────
CAUSE: Package pas disponible pour votre Python/OS

SOLUTIONS:
# Vérifier compatibilité
pip install mathtools --dry-run

# Voir versions disponibles
pip index versions mathtools

# Installer version spécifique
pip install mathtools==0.1.0

# Si package avec C extension, installer wheel pre-compilé
pip install --only-binary :all: mathtools


[X] PROBLÈME: Dépendances conflictuelles irrésolues
─────────────────────────────────────────────────────────────────
DIAGNOSTIC:
pip check
pipdeptree --warn fail

SOLUTIONS:
# Option 1: pip-tools pour résolution
pip install pip-tools
echo "mathtools" > requirements.in
pip-compile requirements.in
pip-sync requirements.txt

# Option 2: Spécifier versions exactes
pip install 'package-a==1.0.0' 'package-b==2.0.0'

# Option 3: Environnement propre
rm -rf .venv
python -m venv .venv
pip install mathtools


═══════════════════════════════════════════════════════════════════
ÉTAPE 20: RÉCAPITULATIF ET WORKFLOW COMPLET
═══════════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────────────────
ÉTAPE 20.1: Timeline détaillée (jour par jour)
─────────────────────────────────────────────────────────────────

[CALENDRIER] JOUR 1: PLANIFICATION ET SETUP (2-3 heures)
─────────────────────────────────────────────────────────────────
[WHITE_SQUARE] Définir objectif et fonctionnalités (30 min)
  - Écrire description en 1 phrase
  - Lister 3-5 fonctionnalités principales
  - Identifier public cible

[WHITE_SQUARE] Choisir nom unique (15 min)
  - Vérifier disponibilité sur pypi.org
  - Vérifier domaine disponible
  - Créer liste de 3-5 noms alternatifs

[WHITE_SQUARE] Setup environnement (30 min)
  - Créer repository GitHub
  - Cloner localement
  - Créer structure dossiers:
    mathtools/
    ├── mathtools/
    │   └── __init__.py
    ├── tests/
    ├── docs/
    ├── .gitignore
    └── README.md

[WHITE_SQUARE] Environnement virtuel (15 min)
  python -m venv .venv
  source .venv/bin/activate
  pip install --upgrade pip

[WHITE_SQUARE] Installer outils dev (15 min)
  pip install pytest black flake8 mypy

[WHITE_SQUARE] Premier commit (15 min)
  git add .
  git commit -m "Initial project structure"
  git push

[CALENDRIER] JOUR 2-3: DÉVELOPPEMENT CORE (4-6 heures)
─────────────────────────────────────────────────────────────────
[WHITE_SQUARE] Écrire code principal (3-4 heures)
  - mathtools/core.py avec fonctions
  - Docstrings Google style
  - Type hints partout
  - Validation entrées

[WHITE_SQUARE] Écrire tests (2 heures)
  - tests/test_core.py
  - Coverage > 80%
  - Cas nominaux + edge cases

[WHITE_SQUARE] Commit réguliers
  git add .
  git commit -m "Add core functions"

[CALENDRIER] JOUR 4: CONFIGURATION ET DOCS (3-4 heures)
─────────────────────────────────────────────────────────────────
[WHITE_SQUARE] Créer pyproject.toml (1 heure)
  - Métadonnées complètes
  - Classifiers PyPI
  - Dépendances

[WHITE_SQUARE] README.md complet (1 heure)
  - Installation
  - Usage examples
  - Features list
  - Links

[WHITE_SQUARE] Fichiers additionnels (1 heure)
  - LICENSE (MIT)
  - CHANGELOG.md
  - CONTRIBUTING.md
  - SECURITY.md

[WHITE_SQUARE] Tests locaux (30 min)
  pip install -e .
  python -c "import mathtools"
  pytest

[CALENDRIER] JOUR 5: DOCUMENTATION SPHINX (3-4 heures)
─────────────────────────────────────────────────────────────────
[WHITE_SQUARE] Setup Sphinx (30 min)
  mkdir docs && cd docs
  sphinx-quickstart

[WHITE_SQUARE] Configuration (1 heure)
  - docs/source/conf.py
  - Extensions autodoc, napoleon
  - Thème sphinx_rtd_theme

[WHITE_SQUARE] Créer pages (2 heures)
  - index.rst
  - installation.rst
  - quickstart.rst
  - api.rst
  - examples.rst

[WHITE_SQUARE] Build et vérif (30 min)
  make html
  python -m http.server 8000 -d build/html

[CALENDRIER] JOUR 6: CI/CD ET QUALITÉ (2-3 heures)
─────────────────────────────────────────────────────────────────
[WHITE_SQUARE] GitHub Actions (1 heure)
  - .github/workflows/tests.yml
  - Matrix Python 3.8-3.12
  - Linux, Windows, macOS

[WHITE_SQUARE] Pre-commit hooks (30 min)
  - .pre-commit-config.yaml
  - Black, isort, flake8
  pre-commit install

[WHITE_SQUARE] Security scans (30 min)
  - .github/workflows/security.yml
  - safety, bandit, pip-audit

[WHITE_SQUARE] Dependabot (30 min)
  - .github/dependabot.yml

[CALENDRIER] JOUR 7: TESTPYPI ET READ THE DOCS (2 heures)
─────────────────────────────────────────────────────────────────
[WHITE_SQUARE] TestPyPI (1 heure)
  - Créer compte test.pypi.org
  - Générer API token
  python -m build
  twine upload --repository testpypi dist/*

[WHITE_SQUARE] Tester installation (15 min)
  pip install -i https://test.pypi.org/simple/ mathtools
  python -c "import mathtools; print(mathtools.__version__)"

[WHITE_SQUARE] Read the Docs (45 min)
  - Créer compte readthedocs.org
  - Importer projet depuis GitHub
  - .readthedocs.yaml
  - Vérifier build

[CALENDRIER] JOUR 8: PUBLICATION FINALE (1-2 heures)
─────────────────────────────────────────────────────────────────
[WHITE_SQUARE] Checklist finale (30 min)
  - Relire toute la documentation
  - Vérifier tous les tests passent
  - Scanner sécurité
  - Valider build

[WHITE_SQUARE] Publication PyPI (15 min)
  python -m build
  twine check dist/*
  twine upload dist/*

[WHITE_SQUARE] GitHub Release (15 min)
  git tag v0.1.0
  git push --tags
  - Créer release sur GitHub
  - Ajouter CHANGELOG

[WHITE_SQUARE] Post-publication (30 min)
  - Tester: pip install mathtools
  - Ajouter badges au README
  - Annoncer sur Twitter/Reddit
  - Partager avec communauté

TOTAL: 20-30 heures réparties sur 8 jours


─────────────────────────────────────────────────────────────────
ÉTAPE 20.2: Workflow complet illustré
─────────────────────────────────────────────────────────────────

```
┌─────────────────────────────────────────────────────────────┐
│  PHASE 1: PRÉPARATION (Jour 1)                              │
└─────────────────────────────────────────────────────────────┘
                           │
                           [BLACK_DOWN-POINTING_TRIANGLE]
                    ┌──────────────┐
                    │ Idée projet  │
                    └──────┬───────┘
                           │
          ┌────────────────┼────────────────┐
          │                │                │
          [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]
   ┌─────────────┐  ┌───────────┐  ┌────────────┐
   │ Nom unique  │  │  GitHub   │  │ Structure  │
   │ disponible  │  │ repository│  │  dossiers  │
   └─────────────┘  └───────────┘  └────────────┘
          │                │                │
          └────────────────┼────────────────┘
                           │
┌─────────────────────────────────────────────────────────────┐
│  PHASE 2: DÉVELOPPEMENT (Jours 2-3)                         │
└─────────────────────────────────────────────────────────────┘
                           │
                           [BLACK_DOWN-POINTING_TRIANGLE]
                    ┌──────────────┐
                    │ Code + Tests │
                    └──────┬───────┘
                           │
          ┌────────────────┼────────────────┐
          │                │                │
          [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]
   ┌─────────────┐  ┌───────────┐  ┌────────────┐
   │ Functions   │  │ Docstrings│  │  Tests     │
   │ Type hints  │  │ Validation│  │  >80% cov  │
   └─────────────┘  └───────────┘  └────────────┘
          │                │                │
          └────────────────┼────────────────┘
                           │
┌─────────────────────────────────────────────────────────────┐
│  PHASE 3: CONFIGURATION (Jour 4)                            │
└─────────────────────────────────────────────────────────────┘
                           │
                           [BLACK_DOWN-POINTING_TRIANGLE]
                    ┌──────────────┐
                    │pyproject.toml│
                    └──────┬───────┘
                           │
          ┌────────────────┼────────────────┐
          │                │                │
          [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]
   ┌─────────────┐  ┌───────────┐  ┌────────────┐
   │  README.md  │  │ LICENSE   │  │ CHANGELOG  │
   │  Complete   │  │ MIT       │  │   .md      │
   └─────────────┘  └───────────┘  └────────────┘
          │                │                │
          └────────────────┼────────────────┘
                           │
┌─────────────────────────────────────────────────────────────┐
│  PHASE 4: DOCUMENTATION (Jour 5)                            │
└─────────────────────────────────────────────────────────────┘
                           │
                           [BLACK_DOWN-POINTING_TRIANGLE]
                    ┌──────────────┐
                    │    Sphinx    │
                    └──────┬───────┘
                           │
          ┌────────────────┼────────────────┐
          │                │                │
          [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]
   ┌─────────────┐  ┌───────────┐  ┌────────────┐
   │  API docs   │  │ Tutorials │  │  Examples  │
   │  (autodoc)  │  │ (manual)  │  │  (tested)  │
   └─────────────┘  └───────────┘  └────────────┘
          │                │                │
          └────────────────┼────────────────┘
                           │
┌─────────────────────────────────────────────────────────────┐
│  PHASE 5: AUTOMATISATION (Jour 6)                           │
└─────────────────────────────────────────────────────────────┘
                           │
                           [BLACK_DOWN-POINTING_TRIANGLE]
                    ┌──────────────┐
                    │GitHub Actions│
                    └──────┬───────┘
                           │
          ┌────────────────┼────────────────┐
          │                │                │
          [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]
   ┌─────────────┐  ┌───────────┐  ┌────────────┐
   │   Tests     │  │ Security  │  │Dependabot  │
   │  CI/CD      │  │  Scans    │  │  Updates   │
   └─────────────┘  └───────────┘  └────────────┘
          │                │                │
          └────────────────┼────────────────┘
                           │
┌─────────────────────────────────────────────────────────────┐
│  PHASE 6: PUBLICATION (Jours 7-8)                           │
└─────────────────────────────────────────────────────────────┘
                           │
                           [BLACK_DOWN-POINTING_TRIANGLE]
                    ┌──────────────┐
                    │  TestPyPI    │
                    └──────┬───────┘
                           │
                           [BLACK_DOWN-POINTING_TRIANGLE]
                    ┌──────────────┐
                    │ Vérifications│
                    └──────┬───────┘
                           │
                           [BLACK_DOWN-POINTING_TRIANGLE]
                    ┌──────────────┐
                    │  PyPI PROD   │
                    └──────┬───────┘
                           │
          ┌────────────────┼────────────────┐
          │                │                │
          [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]                [BLACK_DOWN-POINTING_TRIANGLE]
   ┌─────────────┐  ┌───────────┐  ┌────────────┐
   │   GitHub    │  │Read the   │  │ Promotion  │
   │   Release   │  │  Docs     │  │ Community  │
   └─────────────┘  └───────────┘  └────────────┘

[BRAVO] MODULE PUBLIÉ ET ACCESSIBLE À TOUS! [BRAVO]
```


─────────────────────────────────────────────────────────────────
ÉTAPE 20.3: Première semaine après publication
─────────────────────────────────────────────────────────────────

[CALENDRIER] JOUR 1 POST-PUBLICATION
[WHITE_SQUARE] Monitorer
  - Vérifier page PyPI: https://pypi.org/project/mathtools/
  - Vérifier Read the Docs: https://mathtools.readthedocs.io
  - Tester installation: pip install mathtools

[WHITE_SQUARE] Communication
  - Tweet/post sur réseaux sociaux
  - Post sur r/Python (si pertinent)
  - Annoncer dans communautés pertinentes

[WHITE_SQUARE] Documentation
  - Ajouter badges au README
  - Mettre à jour liens dans docs

[CALENDRIER] JOURS 2-3
[WHITE_SQUARE] Feedback initial
  - Surveiller GitHub issues
  - Répondre aux questions
  - Noter suggestions d'amélioration

[WHITE_SQUARE] Corrections rapides
  - Fixer typos dans docs
  - Corriger bugs mineurs signalés
  - Publier v0.1.1 si nécessaire

[CALENDRIER] JOURS 4-7
[WHITE_SQUARE] Amélioration continue
  - Ajouter exemples demandés
  - Améliorer docs selon feedback
  - Planifier fonctionnalités v0.2.0

[WHITE_SQUARE] Promotion
  - Écrire article de blog
  - Créer tutoriel vidéo (optionnel)
  - Partager use cases

[WHITE_SQUARE] Monitoring
  - Vérifier stats PyPI downloads
  - Analyser traffic Read the Docs
  - Surveiller stars GitHub


─────────────────────────────────────────────────────────────────
ÉTAPE 20.4: Plan de maintenance mensuel
─────────────────────────────────────────────────────────────────

[CALENDRIER] CHAQUE SEMAINE
[WHITE_SQUARE] Lundi matin (30 min)
  - Trier nouvelles issues GitHub
  - Répondre aux questions
  - Merger PRs Dependabot (mineures)

[WHITE_SQUARE] Mercredi (1h si besoin)
  - Travailler sur issues prioritaires
  - Développer nouvelles features

[WHITE_SQUARE] Vendredi (15 min)
  - Vérifier CI/CD (tout vert?)
  - Lire alertes sécurité
  - Planifier semaine suivante

[CALENDRIER] CHAQUE MOIS
[WHITE_SQUARE] Début de mois (2h)
  - Triage complet issues/PRs
  - Planifier release suivante
  - Mettre à jour roadmap

[WHITE_SQUARE] Mi-mois (3-4h)
  - Développer fonctionnalités planifiées
  - Écrire/mettre à jour tests
  - Améliorer documentation

[WHITE_SQUARE] Fin de mois (2h)
  - Préparer release
  - Mettre à jour CHANGELOG
  - Bump version et publier

[CALENDRIER] CHAQUE TRIMESTRE
[WHITE_SQUARE] Review complète (4h)
  - Analyser métriques (downloads, stars)
  - Évaluer feedback utilisateurs
  - Refactoring majeur si nécessaire
  - Mettre à jour dépendances majeures

[WHITE_SQUARE] Sécurité (1h)
  - Audit sécurité complet
  - Revoir SECURITY.md
  - Mettre à jour dépendances

[WHITE_SQUARE] Documentation (2h)
  - Review complète
  - Ajouter tutoriels avancés
  - Mettre à jour screenshots/exemples

[CALENDRIER] CHAQUE ANNÉE
[WHITE_SQUARE] Review stratégique (1 jour)
  - Évaluer direction du projet
  - Décider fonctionnalités majeures
  - Planifier version majeure (v2.0?)
  - Considérer breaking changes

[WHITE_SQUARE] Community (½ jour)
  - Remercier contributeurs
  - Promouvoir projet (conf, blog)
  - Chercher sponsors si approprié


─────────────────────────────────────────────────────────────────
ÉTAPE 20.5: Évolution et croissance du projet
─────────────────────────────────────────────────────────────────

[HAUSSE] PHASE 1: LANCEMENT (v0.1.0 - v0.9.0)
Objectifs:
- Stabiliser API
- Fixer bugs rapidement
- Construire communauté initiale
- Atteindre 100+ downloads/mois

Actions:
- Répondre à toutes les issues rapidement
- Accepter PRs de contributeurs
- Écrire articles/tutoriels
- Présenter dans meetups locaux

Métriques succès:
[OK] 10+ stars GitHub
[OK] 1-2 contributeurs externes
[OK] Zéro issues critiques ouvertes
[OK] Documentation complète

[HAUSSE] PHASE 2: STABILISATION (v1.0.0)
Objectifs:
- API stable (pas de breaking changes)
- Production-ready
- Communauté active
- 1000+ downloads/mois

Actions:
- Extensive testing (coverage > 90%)
- Benchmark performance
- Écrire guide migration depuis 0.x
- Blog post "v1.0 release"

Métriques succès:
[OK] 50+ stars GitHub
[OK] 5+ contributeurs réguliers
[OK] Utilisé en production (témoignages)
[OK] Mentionné dans articles/tutorials

[HAUSSE] PHASE 3: CROISSANCE (v1.x - v2.0)
Objectifs:
- Fonctionnalités avancées
- Écosystème (plugins, intégrations)
- 10,000+ downloads/mois

Actions:
- Créer plugins/extensions
- Intégrations avec outils populaires
- Conférences/talks
- Chercher sponsors

Métriques succès:
[OK] 200+ stars GitHub
[OK] 10+ contributeurs actifs
[OK] Packages dépendants (reverse dependencies)
[OK] Couverture médias tech

[HAUSSE] PHASE 4: MATURITÉ (v2.0+)
Objectifs:
- Standard de l'industrie dans sa niche
- Gouvernance communauté
- Sustainability long terme

Actions:
- Établir core team
- Définir governance
- Program bounty bug
- Funding/sponsors

Métriques succès:
[OK] 1000+ stars GitHub
[OK] 50+ contributeurs
[OK] 100,000+ downloads/mois
[OK] Utilisé par entreprises connues


═══════════════════════════════════════════════════════════════════
ANNEXES
═══════════════════════════════════════════════════════════════════

─────────────────────────────────────────────────────────────────
ANNEXE A: Modèle pyproject.toml complet commenté
─────────────────────────────────────────────────────────────────

# FICHIER: pyproject.toml
# Configuration complète pour un module Python professionnel

[build-system]
# Outils de build (obligatoire)
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
# === INFORMATIONS DE BASE ===
name = "mathtools"
version = "0.1.0"
description = "Utilitaires mathématiques pour Python"
readme = {file = "README.md", content-type = "text/markdown"}
requires-python = ">=3.8"
license = {text = "MIT"}

# === AUTEURS ===
authors = [
    {name = "Votre Nom", email = "votre.email@example.com"},
]
maintainers = [
    {name = "Votre Nom", email = "votre.email@example.com"},
]

# === MOTS-CLÉS ET CLASSIFIERS ===
keywords = ["math", "mathematics", "utilities", "calculations"]
classifiers = [
    # Status de développement
    "Development Status :: 3 - Alpha",
    # "Development Status :: 4 - Beta",
    # "Development Status :: 5 - Production/Stable",
    
    # Public visé
    "Intended Audience :: Developers",
    "Intended Audience :: Science/Research",
    "Intended Audience :: Education",
    
    # Domaine
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Scientific/Engineering :: Mathematics",
    
    # Licence
    "License :: OSI Approved :: MIT License",
    
    # Python
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.8",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3 :: Only",
    
    # OS
    "Operating System :: OS Independent",
    
    # Qualité
    "Typing :: Typed",
]

# === DÉPENDANCES ===
dependencies = [
    # Production dependencies
    # "requests>=2.28.0,<3.0.0",
    # "numpy>=1.20.0",
]

[project.optional-dependencies]
# Dépendances de développement
dev = [
    "pytest>=7.4.0",
    "pytest-cov>=4.1.0",
    "pytest-xdist>=3.3.0",
    "black>=23.7.0",
    "isort>=5.12.0",
    "flake8>=6.1.0",
    "mypy>=1.5.0",
    "pylint>=2.17.0",
]

# Dépendances pour la documentation
docs = [
    "sphinx>=7.1.0",
    "sphinx-rtd-theme>=1.3.0",
    "sphinx-autodoc-typehints>=1.24.0",
]

# Dépendances pour les tests
test = [
    "pytest>=7.4.0",
    "pytest-cov>=4.1.0",
    "hypothesis>=6.82.0",
]

# === URLS ===
[project.urls]
Homepage = "https://github.com/votre-username/mathtools"
Documentation = "https://mathtools.readthedocs.io"
Repository = "https://github.com/votre-username/mathtools"
"Bug Tracker" = "https://github.com/votre-username/mathtools/issues"
Changelog = "https://github.com/votre-username/mathtools/blob/main/CHANGELOG.md"

# === SCRIPTS (ENTRY POINTS) ===
[project.scripts]
mathtools = "mathtools.cli:main"

# === CONFIGURATION SETUPTOOLS ===
[tool.setuptools]
zip-safe = false

[tool.setuptools.packages.find]
where = ["."]
include = ["mathtools*"]
exclude = ["tests*", "docs*"]

[tool.setuptools.package-data]
mathtools = ["py.typed", "data/*.json"]

# === CONFIGURATION PYTEST ===
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
    "--verbose",
    "--strict-markers",
    "--cov=mathtools",
    "--cov-branch",
    "--cov-report=term-missing",
    "--cov-report=html",
    "--cov-report=xml",
]
markers = [
    "slow: marks tests as slow",
    "integration: marks tests as integration tests",
]

# === CONFIGURATION COVERAGE ===
[tool.coverage.run]
source = ["mathtools"]
omit = ["tests/*", "*/test_*.py"]

[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "raise AssertionError",
    "raise NotImplementedError",
    "if __name__ == .__main__.:",
    "if TYPE_CHECKING:",
]

# === CONFIGURATION BLACK ===
[tool.black]
line-length = 88
target-version = ["py38", "py39", "py310", "py311", "py312"]
include = '\.pyi?# Fichier: python_cheats/cheatsheets/module_part2.txt
# Guide Complet - Création et Publication de Modules Python (PARTIE 2)
# Sécurité, Maintenance et Bonnes Pratiques Avancées

═══════════════════════════════════════════════════════════════════
Suite et fin dans la prochaine section...
═══════════════════════════════════════════════════════════════════
    