# Fichier: python_cheats/cheatsheets/module.txt
# Cheatsheet Création et Publication de Modules Python - Guide Complet


[OK] STRUCTURE DE BASE D'UN MODULE PYTHON

# Module simple (un seul fichier)
mymodule.py                     # Fichier Python = module

# Package (dossier avec __init__.py)
mypackage/
├── __init__.py                 # Fait du dossier un package
├── module1.py
└── module2.py

# Projet complet (distribution)
myproject/
├── src/                        # Code source (recommandé)
│   └── mypackage/
│       ├── __init__.py
│       ├── core.py
│       └── utils.py
├── tests/                      # Tests
│   ├── __init__.py
│   ├── test_core.py
│   └── test_utils.py
├── docs/                       # Documentation
│   ├── conf.py
│   ├── index.rst
│   └── api.rst
├── examples/                   # Exemples d'utilisation
│   └── basic_usage.py
├── .gitignore
├── LICENSE                     # Licence (obligatoire pour PyPI)
├── README.md                   # Description (obligatoire)
├── pyproject.toml             # Configuration moderne (PEP 518)
├── setup.py                   # Configuration legacy (optionnel)
├── setup.cfg                  # Configuration alternative
├── MANIFEST.in                # Fichiers additionnels à inclure
├── requirements.txt           # Dépendances
└── requirements-dev.txt       # Dépendances développement


[OK] CRÉER UN MODULE SIMPLE

# === Fichier unique mymodule.py ===
"""
Module de démonstration.

Ce module fournit des fonctions utiles pour...
"""

__version__ = "0.1.0"
__author__ = "Votre Nom"
__email__ = "votre@email.com"

def hello(name):
    """Dit bonjour à quelqu'un.
    
    Args:
        name (str): Le nom de la personne
        
    Returns:
        str: Message de bienvenue
        
    Examples:
        >>> hello("Alice")
        'Bonjour Alice!'
    """
    return f"Bonjour {name}!"

def add(a, b):
    """Additionne deux nombres.
    
    Args:
        a (int/float): Premier nombre
        b (int/float): Deuxième nombre
        
    Returns:
        int/float: Somme de a et b
    """
    return a + b

# === Utilisation ===
# Dans un autre fichier:
import mymodule

print(mymodule.hello("Alice"))
print(mymodule.add(5, 3))

# Ou:
from mymodule import hello, add

print(hello("Bob"))
print(add(10, 20))


[OK] CRÉER UN PACKAGE

# === Structure du package ===
mypackage/
├── __init__.py
├── core.py
├── utils.py
└── constants.py

# === mypackage/__init__.py ===
"""
MyPackage - Un super package Python.

Description complète du package...
"""

__version__ = "0.1.0"
__author__ = "Votre Nom"
__all__ = ["hello", "add", "format_text"]  # Exports publics

# Importer depuis les sous-modules pour faciliter l'accès
from .core import hello, add
from .utils import format_text

# Ou importer tout:
from .core import *
from .utils import *

# === mypackage/core.py ===
"""Module principal avec fonctionnalités de base."""

def hello(name):
    """Dit bonjour."""
    return f"Bonjour {name}!"

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

# === mypackage/utils.py ===
"""Utilitaires et fonctions d'aide."""

def format_text(text, uppercase=False):
    """Formate du texte.
    
    Args:
        text (str): Texte à formater
        uppercase (bool): Mettre en majuscules
        
    Returns:
        str: Texte formaté
    """
    if uppercase:
        return text.upper()
    return text.lower()

# === mypackage/constants.py ===
"""Constantes utilisées dans le package."""

DEFAULT_TIMEOUT = 30
MAX_RETRIES = 3
API_VERSION = "v1"

# === Utilisation ===
import mypackage

print(mypackage.hello("Alice"))
print(mypackage.format_text("Hello"))

# Ou:
from mypackage import hello, add
from mypackage.constants import DEFAULT_TIMEOUT


[OK] SOUS-PACKAGES (STRUCTURE HIÉRARCHIQUE)

# === Structure avec sous-packages ===
mypackage/
├── __init__.py
├── core/
│   ├── __init__.py
│   ├── engine.py
│   └── processor.py
├── utils/
│   ├── __init__.py
│   ├── helpers.py
│   └── validators.py
└── api/
    ├── __init__.py
    ├── client.py
    └── exceptions.py

# === mypackage/__init__.py ===
"""Package principal."""

__version__ = "0.1.0"

# Importer depuis sous-packages
from .core import Engine, Processor
from .api import Client
from .api.exceptions import APIError

# === mypackage/core/__init__.py ===
"""Sous-package core."""

from .engine import Engine
from .processor import Processor

__all__ = ["Engine", "Processor"]

# === mypackage/api/__init__.py ===
"""Sous-package API."""

from .client import Client
from .exceptions import APIError, ValidationError

__all__ = ["Client", "APIError", "ValidationError"]

# === Utilisation ===
from mypackage import Engine, Client
from mypackage.core import Processor
from mypackage.api.exceptions import APIError


[OK] PYPROJECT.TOML (CONFIGURATION MODERNE - PEP 518)

# === pyproject.toml complet ===
[build-system]
# Outil de build (obligatoire)
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

# Ou avec poetry:
# requires = ["poetry-core>=1.0.0"]
# build-backend = "poetry.core.masonry.api"

# Ou avec flit:
# requires = ["flit_core>=3.2"]
# build-backend = "flit_core.buildapi"

# Ou avec hatchling:
# requires = ["hatchling"]
# build-backend = "hatchling.build"

[project]
# Métadonnées du projet (obligatoire)
name = "mypackage"
version = "0.1.0"
description = "Un super package Python"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
    {name = "Votre Nom", email = "votre@email.com"}
]
maintainers = [
    {name = "Mainteneur", email = "mainteneur@email.com"}
]
keywords = ["python", "package", "example"]
classifiers = [
    "Development Status :: 3 - Alpha",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "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",
    "Operating System :: OS Independent",
    "Topic :: Software Development :: Libraries :: Python Modules",
]

# Dépendances (obligatoire, peut être vide)
dependencies = [
    "requests>=2.28.0",
    "click>=8.0.0",
    "pydantic>=2.0.0",
]

# Dépendances optionnelles (extras)
[project.optional-dependencies]
dev = [
    "pytest>=7.0.0",
    "pytest-cov>=4.0.0",
    "black>=23.0.0",
    "flake8>=6.0.0",
    "mypy>=1.0.0",
    "isort>=5.12.0",
]
docs = [
    "sphinx>=7.0.0",
    "sphinx-rtd-theme>=1.3.0",
    "sphinx-autodoc-typehints>=1.24.0",
]
test = [
    "pytest>=7.0.0",
    "pytest-cov>=4.0.0",
    "pytest-mock>=3.11.0",
]

# URLs du projet
[project.urls]
Homepage = "https://github.com/username/mypackage"
Documentation = "https://mypackage.readthedocs.io"
Repository = "https://github.com/username/mypackage"
"Bug Tracker" = "https://github.com/username/mypackage/issues"
Changelog = "https://github.com/username/mypackage/blob/main/CHANGELOG.md"

# Points d'entrée (scripts CLI)
[project.scripts]
mypackage = "mypackage.cli:main"
mypackage-admin = "mypackage.admin:main"

# Points d'entrée GUI (optionnel)
[project.gui-scripts]
mypackage-gui = "mypackage.gui:main"

# Entry points pour plugins
[project.entry-points."mypackage.plugins"]
plugin1 = "mypackage_plugin1:Plugin1"

# Configuration setuptools
[tool.setuptools]
package-dir = {"" = "src"}  # Code source dans src/
packages = ["mypackage"]     # Packages à inclure

# Ou découverte automatique:
[tool.setuptools.packages.find]
where = ["src"]
include = ["mypackage*"]
exclude = ["tests*"]

# Fichiers de données à inclure
[tool.setuptools.package-data]
mypackage = ["data/*.json", "templates/*.html"]

# Configuration pytest
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "-v --cov=mypackage --cov-report=html --cov-report=term"

# Configuration black
[tool.black]
line-length = 88
target-version = ["py38", "py39", "py310", "py311"]
include = '\.pyi?$'
extend-exclude = '''
/(
  \.git
  | \.venv
  | build
  | dist
)/
'''

# Configuration isort
[tool.isort]
profile = "black"
line_length = 88
multi_line_output = 3
include_trailing_comma = true

# Configuration mypy
[tool.mypy]
python_version = "3.8"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true

# Configuration coverage
[tool.coverage.run]
source = ["src"]
omit = ["*/tests/*"]

[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "raise AssertionError",
    "raise NotImplementedError",
    "if __name__ == .__main__.:",
]


[OK] SETUP.PY (CONFIGURATION LEGACY)

# === setup.py minimal (legacy, toujours utile) ===
from setuptools import setup, find_packages

setup(
    name="mypackage",
    version="0.1.0",
    packages=find_packages(where="src"),
    package_dir={"": "src"},
)

# === setup.py complet ===
from setuptools import setup, find_packages
import os

# Lire README pour long_description
with open("README.md", "r", encoding="utf-8") as fh:
    long_description = fh.read()

# Lire requirements depuis fichier
def read_requirements(filename):
    with open(filename, "r") as f:
        return [line.strip() for line in f if line.strip() and not line.startswith("#")]

setup(
    # Métadonnées de base
    name="mypackage",
    version="0.1.0",
    author="Votre Nom",
    author_email="votre@email.com",
    description="Un super package Python",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/username/mypackage",
    project_urls={
        "Bug Tracker": "https://github.com/username/mypackage/issues",
        "Documentation": "https://mypackage.readthedocs.io",
        "Source Code": "https://github.com/username/mypackage",
    },
    
    # Packages et code
    packages=find_packages(where="src"),
    package_dir={"": "src"},
    
    # Ou spécifier manuellement:
    # packages=["mypackage", "mypackage.core", "mypackage.utils"],
    
    # Inclure fichiers de données
    package_data={
        "mypackage": ["data/*.json", "templates/*.html"],
    },
    include_package_data=True,  # Utilise MANIFEST.in
    
    # Version Python requise
    python_requires=">=3.8",
    
    # Dépendances
    install_requires=read_requirements("requirements.txt"),
    
    # Dépendances optionnelles
    extras_require={
        "dev": read_requirements("requirements-dev.txt"),
        "docs": [
            "sphinx>=7.0.0",
            "sphinx-rtd-theme>=1.3.0",
        ],
        "test": [
            "pytest>=7.0.0",
            "pytest-cov>=4.0.0",
        ],
    },
    
    # Scripts en ligne de commande
    entry_points={
        "console_scripts": [
            "mypackage=mypackage.cli:main",
            "mypackage-admin=mypackage.admin:main",
        ],
    },
    
    # Classifiers (catégorisation sur PyPI)
    classifiers=[
        "Development Status :: 3 - Alpha",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: MIT License",
        "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",
        "Operating System :: OS Independent",
        "Topic :: Software Development :: Libraries :: Python Modules",
    ],
    
    # Licence
    license="MIT",
    
    # Mots-clés pour recherche
    keywords="python package example tutorial",
    
    # Compatibilité zip
    zip_safe=False,
)

# === Avec version dynamique depuis __init__.py ===
import re
from pathlib import Path

def get_version():
    """Lit la version depuis __init__.py"""
    init_file = Path("src/mypackage/__init__.py")
    content = init_file.read_text()
    match = re.search(r'^__version__ = ["\']([^"\']+)["\']', content, re.M)
    if match:
        return match.group(1)
    raise RuntimeError("Version non trouvée")

setup(
    name="mypackage",
    version=get_version(),
    # ... reste de la config
)


[OK] SETUP.CFG (CONFIGURATION DÉCLARATIVE)

# === setup.cfg ===
[metadata]
name = mypackage
version = 0.1.0
author = Votre Nom
author_email = votre@email.com
description = Un super package Python
long_description = file: README.md
long_description_content_type = text/markdown
url = https://github.com/username/mypackage
project_urls =
    Bug Tracker = https://github.com/username/mypackage/issues
    Documentation = https://mypackage.readthedocs.io
classifiers =
    Development Status :: 3 - Alpha
    Intended Audience :: Developers
    License :: OSI Approved :: MIT License
    Programming Language :: Python :: 3
    Programming Language :: Python :: 3.8
    Programming Language :: Python :: 3.9
    Programming Language :: Python :: 3.10
    Programming Language :: Python :: 3.11
license = MIT
license_files = LICENSE
keywords = python, package, example

[options]
packages = find:
package_dir =
    = src
python_requires = >=3.8
install_requires =
    requests>=2.28.0
    click>=8.0.0
include_package_data = True
zip_safe = False

[options.packages.find]
where = src
exclude =
    tests*
    docs*

[options.extras_require]
dev =
    pytest>=7.0.0
    black>=23.0.0
    flake8>=6.0.0
docs =
    sphinx>=7.0.0
    sphinx-rtd-theme>=1.3.0

[options.entry_points]
console_scripts =
    mypackage = mypackage.cli:main

[options.package_data]
mypackage = data/*.json, templates/*.html


[OK] MANIFEST.IN (INCLURE FICHIERS ADDITIONNELS)

# === MANIFEST.in ===
# Inclure fichiers de documentation
include README.md
include LICENSE
include CHANGELOG.md
include CONTRIBUTING.md

# Inclure requirements
include requirements.txt
include requirements-dev.txt

# Inclure fichiers de données
recursive-include mypackage/data *
recursive-include mypackage/templates *
recursive-include mypackage/static *

# Inclure tests (optionnel)
recursive-include tests *.py

# Inclure docs (optionnel)
recursive-include docs *.rst *.md *.py
prune docs/_build

# Exclure fichiers inutiles
global-exclude *.pyc
global-exclude *.pyo
global-exclude __pycache__
global-exclude .DS_Store
global-exclude .git*
global-exclude *.so
global-exclude *.egg-info


[OK] README.MD (OBLIGATOIRE POUR PYPI)

# === README.md structure recommandée ===
```markdown
# MyPackage

[![PyPI version](https://badge.fury.io/py/mypackage.svg)](https://pypi.org/project/mypackage/)
[![Python versions](https://img.shields.io/pypi/pyversions/mypackage.svg)](https://pypi.org/project/mypackage/)
[![License](https://img.shields.io/pypi/l/mypackage.svg)](https://github.com/username/mypackage/blob/main/LICENSE)
[![Documentation](https://readthedocs.org/projects/mypackage/badge/)](https://mypackage.readthedocs.io)

Description courte et percutante de votre package.

## Fonctionnalités

- * Fonctionnalité 1
- [RAPIDE] Fonctionnalité 2
- [OUTIL] Fonctionnalité 3

## Installation

```bash
pip install mypackage
```

Pour développement:
```bash
pip install mypackage[dev]
```

## Utilisation rapide

```python
from mypackage import hello

print(hello("World"))
# Output: Bonjour World!
```

## Exemples

### Exemple basique
```python
from mypackage import add

result = add(5, 3)
print(result)  # 8
```

### Exemple avancé
```python
from mypackage.core import Engine

engine = Engine()
engine.process()
```

## Documentation

Documentation complète disponible sur [Read the Docs](https://mypackage.readthedocs.io).

## Développement

### Installation pour développement

```bash
git clone https://github.com/username/mypackage.git
cd mypackage
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
```

### Lancer les tests

```bash
pytest
pytest --cov=mypackage
```

### Formater le code

```bash
black src/ tests/
isort src/ tests/
flake8 src/ tests/
```

## Contribuer

Les contributions sont bienvenues! Voir [CONTRIBUTING.md](CONTRIBUTING.md).

## Licence

Ce projet est sous licence MIT - voir [LICENSE](LICENSE).

## Auteurs

- **Votre Nom** - *Travail initial* - [username](https://github.com/username)

## Changelog

Voir [CHANGELOG.md](CHANGELOG.md).
```


[OK] LICENCE (OBLIGATOIRE POUR PYPI)

# === Choisir une licence ===
# Visitez: https://choosealicense.com/

# Licences populaires:
# - MIT: Permissive, très populaire
# - Apache 2.0: Permissive avec protection brevets
# - GPL v3: Copyleft fort
# - BSD 3-Clause: Permissive simple
# - MPL 2.0: Copyleft faible

# === LICENSE (MIT) ===
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.


[OK] VERSIONING (SEMVER)

# === Semantic Versioning (SemVer) ===
# Format: MAJOR.MINOR.PATCH

# MAJOR: Changements incompatibles (breaking changes)
1.0.0 -> 2.0.0

# MINOR: Nouvelles fonctionnalités (compatible)
1.0.0 -> 1.1.0

# PATCH: Corrections de bugs
1.0.0 -> 1.0.1

# Versions pré-release
1.0.0-alpha
1.0.0-beta
1.0.0-rc.1 (release candidate)

# Exemples:
0.1.0    # Développement initial
0.2.0    # Nouvelles fonctionnalités (pre-1.0)
1.0.0    # Première version stable
1.0.1    # Correction de bug
1.1.0    # Nouvelle fonctionnalité
2.0.0    # Breaking change

# === Gérer la version dans le code ===

# Méthode 1: __init__.py (simple)
# src/mypackage/__init__.py
__version__ = "0.1.0"

# Méthode 2: _version.py (séparé)
# src/mypackage/_version.py
__version__ = "0.1.0"

# src/mypackage/__init__.py
from ._version import __version__

# Méthode 3: setuptools-scm (depuis Git tags)
# pyproject.toml
[build-system]
requires = ["setuptools>=61.0", "setuptools-scm"]

[tool.setuptools_scm]

# Utilisation
from importlib.metadata import version
__version__ = version("mypackage")


[OK] BUILD DU PACKAGE

# === Installer outils de build ===
pip install --upgrade pip setuptools wheel build twine

# === Builder le package ===
# Avec build (recommandé - PEP 517)
python -m build

# Ou avec setuptools (legacy)
python setup.py sdist bdist_wheel

# Fichiers générés dans dist/:
# mypackage-0.1.0.tar.gz          # Source distribution (sdist)
# mypackage-0.1.0-py3-none-any.whl # Wheel distribution

# === Vérifier le build ===
# Lister contenu de la wheel
unzip -l dist/mypackage-0.1.0-py3-none-any.whl

# Vérifier avec twine
twine check dist/*

# === Installer localement pour tester ===
pip install dist/mypackage-0.1.0-py3-none-any.whl

# Ou installer en mode éditable (développement)
pip install -e .
pip install -e ".[dev]"         # Avec extras dev


[OK] TESTER LOCALEMENT AVANT PUBLICATION

# === Installer en mode éditable ===
# Depuis la racine du projet
pip install -e .

# Avec dépendances dev
pip install -e ".[dev]"

# === Tester l'import ===
python
>>> import mypackage
>>> mypackage.__version__
'0.1.0'
>>> from mypackage import hello
>>> hello("Test")
'Bonjour Test!'

# === Tester les scripts CLI ===
mypackage --help
mypackage --version

# === Lancer les tests ===
pytest
pytest -v
pytest --cov=mypackage

# === Vérifier la qualité du code ===
# Black (formatage)
black src/ tests/

# isort (imports)
isort src/ tests/

# flake8 (linting)
flake8 src/ tests/

# mypy (type checking)
mypy src/

# pylint (analyse approfondie)
pylint src/


[OK] PUBLICATION SUR PYPI

# === 1. CRÉER COMPTE PYPI ===
# Production: https://pypi.org/account/register/
# Test: https://test.pypi.org/account/register/

# === 2. CONFIGURER ~/.pypirc (optionnel) ===
[distutils]
index-servers =
    pypi
    testpypi

[pypi]
username = __token__
password = pypi-VOTRE_TOKEN_ICI

[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-VOTRE_TOKEN_TEST_ICI

# === 3. OBTENIR TOKEN API ===
# PyPI: https://pypi.org/manage/account/token/
# Créer un token avec scope "Entire account" ou projet spécifique

# === 4. BUILDER LE PACKAGE ===
# Nettoyer anciens builds
rm -rf dist/ build/ *.egg-info

# Builder
python -m build

# === 5. TESTER SUR TEST.PYPI ===
# Uploader sur TestPyPI
twine upload --repository testpypi dist/*

# Ou avec token direct
twine upload --repository testpypi dist/* --username __token__ --password pypi-TOKEN

# Tester installation depuis TestPyPI
pip install --index-url https://test.pypi.org/simple/ mypackage

# Tester avec dépendances depuis PyPI principal
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ mypackage

# === 6. PUBLIER SUR PYPI PRODUCTION ===
# Uploader
twine upload dist/*

# Ou avec token direct
twine upload dist/* --username __token__ --password pypi-TOKEN

# === 7. VÉRIFIER PUBLICATION ===
# Visiter: https://pypi.org/project/mypackage/

# Installer depuis PyPI
pip install mypackage

# === 8. METTRE À JOUR VERSION ===
# 1. Modifier version dans __init__.py ou pyproject.toml
# 2. Commit et tag Git
git add .
git commit -m "Release version 0.2.0"
git tag v0.2.0
git push origin main --tags

# 3. Rebuild et republier
rm -rf dist/
python -m build
twine upload dist/*


[OK] AUTOMATISATION AVEC GITHUB ACTIONS

# === .github/workflows/publish.yml ===
name: Publish to PyPI

on:
  release:
    types: [published]

jobs:
  pypi-publish:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Python
      uses: actions/setup-python@v4
      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. Ajouter secret PYPI_API_TOKEN dans GitHub
#    Settings -> Secrets -> New repository secret
# 2. Créer release sur GitHub
#    Releases -> Create new release -> Tag: v0.1.0


[OK] DOCUMENTATION AVEC SPHINX

# === 1. INSTALLER SPHINX ===
pip install sphinx sphinx-rtd-theme sphinx-autodoc-typehints

# === 2. INITIALISER SPHINX ===
cd docs/
sphinx-quickstart

# Questions:
# > Separate source and build directories (y/n) [n]: y
# > Project name: MyPackage
# > Author name(s): Votre Nom
# > Project release []: 0.1.0
# > Project language [en]: fr (ou en)

# Structure créée:
docs/
├── source/
│   ├── conf.py         # Configuration
│   ├── index.rst       # Page d'accueil
│   └── _static/
├── build/
└── Makefile

# === 3. CONFIGURER conf.py ===
# docs/source/conf.py

import os
import sys

# Ajouter chemin vers le code source
sys.path.insert(0, os.path.abspath('../../src'))

# Informations projet
project = 'MyPackage'
copyright = '2025, Votre Nom'
author = 'Votre Nom'
release = '0.1.0'

# Extensions
extensions = [
    'sphinx.ext.autodoc',           # Documentation automatique
    'sphinx.ext.napoleon',          # Google/NumPy docstrings
    'sphinx.ext.viewcode',          # Liens vers code source
    'sphinx.ext.intersphinx',       # Liens vers autre docs
    'sphinx_autodoc_typehints',     # Type hints dans docs
    'sphinx.ext.todo',              # TODOs
    'sphinx.ext.coverage',          # Couverture documentation
]

# Thème
html_theme = 'sphinx_rtd_theme'  # Read the Docs theme

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

# Fichiers statiques
html_static_path = ['_static']

# Templates
templates_path = ['_templates']

# Langue
language = 'fr'  # ou 'en'

# Intersphinx (liens vers autres docs)
intersphinx_mapping = {
    'python': ('https://docs.python.org/3', None),
    'requests': ('https://requests.readthedocs.io/en/latest/', None),
}

# Napoleon (Google/NumPy docstrings)
napoleon_google_docstring = True
napoleon_numpy_docstring = True
napoleon_include_init_with_doc = True

# Autodoc
autodoc_default_options = {
    'members': True,
    'member-order': 'bysource',
    'special-members': '__init__',
    'undoc-members': True,
    'exclude-members': '__weakref__'
}

# === 4. CRÉER index.rst ===
# docs/source/index.rst

Welcome to MyPackage's documentation!
======================================

.. toctree::
   :maxdepth: 2
   :caption: Contents:

   installation
   quickstart
   api
   examples
   contributing
   changelog

Introduction
------------

MyPackage est un super package Python qui fait des choses incroyables.

Fonctionnalités principales:

* * Fonctionnalité 1
* [RAPIDE] Fonctionnalité 2
* [OUTIL] Fonctionnalité 3

Indices and tables
==================

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

# === 5. CRÉER installation.rst ===
# docs/source/installation.rst

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

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

Installer depuis PyPI::

    pip install mypackage

Installation développement
--------------------------

Installer depuis GitHub::

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

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

* Python 3.8+
* pip

Dépendances
-----------

Les dépendances suivantes seront installées automatiquement:

* requests >= 2.28.0
* click >= 8.0.0

# === 6. CRÉER quickstart.rst ===
# docs/source/quickstart.rst

Quick Start
===========

Import basique
--------------

.. code-block:: python

    from mypackage import hello
    
    print(hello("World"))
    # Output: Bonjour World!

Utilisation avancée
-------------------

.. code-block:: python

    from mypackage.core import Engine
    
    engine = Engine()
    result = engine.process()

Configuration
-------------

.. code-block:: python

    from mypackage import configure
    
    configure({
        'timeout': 30,
        'retries': 3
    })

# === 7. CRÉER api.rst (documentation API) ===
# docs/source/api.rst

API Reference
=============

Core Module
-----------

.. automodule:: mypackage.core
   :members:
   :undoc-members:
   :show-inheritance:

Utils Module
------------

.. automodule:: mypackage.utils
   :members:
   :undoc-members:
   :show-inheritance:

API Module
----------

.. automodule:: mypackage.api
   :members:
   :undoc-members:
   :show-inheritance:

Exceptions
----------

.. automodule:: mypackage.exceptions
   :members:
   :undoc-members:
   :show-inheritance:

# === 8. CRÉER examples.rst ===
# docs/source/examples.rst

Examples
========

Exemple basique
---------------

.. code-block:: python

    from mypackage import hello, add
    
    # Dire bonjour
    greeting = hello("Alice")
    print(greeting)
    
    # Additionner des nombres
    result = add(5, 3)
    print(result)

Exemple avec configuration
--------------------------

.. code-block:: python

    from mypackage import MyClass
    
    # Créer instance avec config
    obj = MyClass(
        timeout=60,
        retries=5
    )
    
    # Utiliser
    result = obj.process()

Exemple asynchrone
------------------

.. code-block:: python

    import asyncio
    from mypackage import AsyncClient
    
    async def main():
        client = AsyncClient()
        result = await client.fetch()
        return result
    
    asyncio.run(main())

# === 9. BUILDER LA DOCUMENTATION ===
cd docs/

# Builder HTML
make html

# Ou sur Windows
.\make.bat html

# Documentation générée dans: docs/build/html/

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

# Windows:
start build/html/index.html

# === 10. AUTRES FORMATS ===
# PDF
make latexpdf

# EPUB
make epub

# Man pages
make man

# === 11. NETTOYER ===
make clean


[OK] PUBLICATION SUR READ THE DOCS

# === 1. PRÉPARER LE PROJET ===

# Créer .readthedocs.yaml à la racine
# .readthedocs.yaml

version: 2

build:
  os: ubuntu-22.04
  tools:
    python: "3.11"
  jobs:
    post_install:
      - pip install -e ".[docs]"

sphinx:
  configuration: docs/source/conf.py
  fail_on_warning: false

python:
  install:
    - method: pip
      path: .
      extra_requirements:
        - docs

formats:
  - pdf
  - epub

# === 2. CRÉER requirements-docs.txt (si non dans pyproject.toml) ===
# requirements-docs.txt
sphinx>=7.0.0
sphinx-rtd-theme>=1.3.0
sphinx-autodoc-typehints>=1.24.0

# === 3. S'INSCRIRE SUR READ THE DOCS ===
# Aller sur: https://readthedocs.org/
# Se connecter avec GitHub

# === 4. IMPORTER LE PROJET ===
# 1. Cliquer "Import a Project"
# 2. Sélectionner le dépôt GitHub
# 3. Configurer:
#    - Name: mypackage
#    - Repository URL: https://github.com/username/mypackage
#    - Default branch: main
#    - Documentation type: Sphinx Html

# === 5. CONFIGURER WEBHOOK (automatique) ===
# Read the Docs configure automatiquement un webhook GitHub
# pour rebuild à chaque push

# === 6. BUILDER ===
# Cliquer "Build version"
# Attendre le build (quelques minutes)

# === 7. VÉRIFIER ===
# Documentation disponible sur:
# https://mypackage.readthedocs.io/

# === 8. PERSONNALISER ===
# Dans Admin -> Advanced Settings:
# - Default version: latest
# - Privacy Level: Public
# - Analytics code: (Google Analytics)

# === 9. VERSIONS ===
# Read the Docs build automatiquement:
# - latest: branche main/master
# - stable: dernière release tag
# - Chaque tag: version spécifique

# === 10. BADGE POUR README ===
[![Documentation Status](https://readthedocs.org/projects/mypackage/badge/?version=latest)](https://mypackage.readthedocs.io/en/latest/?badge=latest)

# === 11. DOMAINE PERSONNALISÉ (optionnel) ===
# Admin -> Domains -> Add domain
# Configurer CNAME DNS: docs.mypackage.com -> mypackage.readthedocs.io


[OK] DOCSTRINGS (STYLE GOOGLE)

# === Fonction simple ===
def add(a, b):
    """Additionne deux nombres.
    
    Args:
        a (int): Premier nombre
        b (int): Deuxième nombre
        
    Returns:
        int: Somme de a et b
        
    Examples:
        >>> add(5, 3)
        8
        >>> add(-1, 1)
        0
    """
    return a + b

# === Fonction avec exceptions ===
def divide(a, b):
    """Divise deux nombres.
    
    Args:
        a (float): Numérateur
        b (float): Dénominateur
        
    Returns:
        float: Résultat de la division
        
    Raises:
        ValueError: Si b est zéro
        TypeError: Si a ou b ne sont pas des nombres
        
    Examples:
        >>> divide(10, 2)
        5.0
        >>> divide(10, 0)
        Traceback (most recent call last):
        ValueError: Division par zéro impossible
    """
    if b == 0:
        raise ValueError("Division par zéro impossible")
    return a / b

# === Classe ===
class Calculator:
    """Calculatrice simple.
    
    Cette classe fournit des opérations mathématiques de base.
    
    Attributes:
        precision (int): Nombre de décimales pour les résultats
        history (list): Historique des opérations
        
    Examples:
        >>> calc = Calculator(precision=2)
        >>> calc.add(5, 3)
        8.0
    """
    
    def __init__(self, precision=2):
        """Initialise la calculatrice.
        
        Args:
            precision (int, optional): Décimales à garder. Défaut 2.
        """
        self.precision = precision
        self.history = []
    
    def add(self, a, b):
        """Additionne deux nombres.
        
        Args:
            a (float): Premier nombre
            b (float): Deuxième nombre
            
        Returns:
            float: Somme arrondie selon precision
        """
        result = round(a + b, self.precision)
        self.history.append(f"{a} + {b} = {result}")
        return result

# === Avec types hints ===
from typing import List, Optional, Union

def process_data(
    data: List[str],
    separator: str = ",",
    max_items: Optional[int] = None
) -> Union[List[str], str]:
    """Traite une liste de données.
    
    Args:
        data: Liste de chaînes à traiter
        separator: Séparateur pour jointure
        max_items: Nombre maximum d'items (None = tous)
        
    Returns:
        Liste traitée ou chaîne jointe
        
    Note:
        Si max_items est None, tous les items sont traités.
        
    Warning:
        Les données vides sont ignorées.
    """
    filtered = [d for d in data if d]
    if max_items:
        filtered = filtered[:max_items]
    return separator.join(filtered)


[OK] DOCSTRINGS (STYLE NUMPY)

# === Fonction avec NumPy style ===
def calculate(a, b, operation='add'):
    """
    Effectue une opération mathématique.
    
    Parameters
    ----------
    a : float
        Premier opérande
    b : float
        Deuxième opérande
    operation : str, optional
        Type d'opération ('add', 'subtract', 'multiply', 'divide')
        Par défaut 'add'
    
    Returns
    -------
    float
        Résultat de l'opération
    
    Raises
    ------
    ValueError
        Si operation n'est pas valide
    ZeroDivisionError
        Si division par zéro
    
    See Also
    --------
    add : Addition simple
    multiply : Multiplication simple
    
    Notes
    -----
    Cette fonction utilise les opérateurs Python standards.
    
    Examples
    --------
    >>> calculate(10, 5, 'add')
    15.0
    >>> calculate(10, 5, 'multiply')
    50.0
    """
    if operation == 'add':
        return a + b
    elif operation == 'subtract':
        return a - b
    elif operation == 'multiply':
        return a * b
    elif operation == 'divide':
        return a / b
    else:
        raise ValueError(f"Opération invalide: {operation}")


[OK] TESTS AVEC PYTEST

# === Structure tests/ ===
tests/
├── __init__.py
├── conftest.py              # Fixtures partagées
├── test_core.py
├── test_utils.py
└── test_integration.py

# === tests/conftest.py (fixtures) ===
import pytest
from mypackage import MyClass

@pytest.fixture
def sample_data():
    """Fixture avec données de test."""
    return [1, 2, 3, 4, 5]

@pytest.fixture
def myclass_instance():
    """Fixture avec instance de classe."""
    return MyClass(timeout=30)

# === tests/test_core.py ===
import pytest
from mypackage import hello, add
from mypackage.exceptions import ValidationError

def test_hello():
    """Test fonction hello."""
    assert hello("World") == "Bonjour World!"
    assert hello("Alice") == "Bonjour Alice!"

def test_hello_empty():
    """Test hello avec chaîne vide."""
    with pytest.raises(ValueError):
        hello("")

def test_add():
    """Test fonction add."""
    assert add(5, 3) == 8
    assert add(-1, 1) == 0
    assert add(0, 0) == 0

def test_add_floats():
    """Test add avec floats."""
    assert add(0.1, 0.2) == pytest.approx(0.3)

@pytest.mark.parametrize("a,b,expected", [
    (1, 2, 3),
    (0, 0, 0),
    (-1, 1, 0),
    (100, 200, 300),
])
def test_add_parametrized(a, b, expected):
    """Test add avec plusieurs cas."""
    assert add(a, b) == expected

def test_with_fixture(sample_data):
    """Test avec fixture."""
    assert len(sample_data) == 5
    assert sum(sample_data) == 15

# === tests/test_integration.py ===
import pytest
from mypackage import MyClass

@pytest.mark.integration
def test_full_workflow():
    """Test workflow complet."""
    obj = MyClass()
    obj.setup()
    result = obj.process()
    assert result is not None
    obj.cleanup()

# === Configuration pytest ===
# pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = """
    -v
    --strict-markers
    --cov=mypackage
    --cov-report=html
    --cov-report=term-missing
"""
markers = [
    "slow: marks tests as slow",
    "integration: marks tests as integration tests",
]

# === Lancer tests ===
# Tous les tests
pytest

# Tests spécifiques
pytest tests/test_core.py
pytest tests/test_core.py::test_hello

# Avec couverture
pytest --cov=mypackage

# Rapport HTML couverture
pytest --cov=mypackage --cov-report=html
open htmlcov/index.html

# Tests lents seulement
pytest -m slow

# Exclure tests lents
pytest -m "not slow"

# Mode verbose
pytest -v

# Arrêter au premier échec
pytest -x

# Parallélisation
pip install pytest-xdist
pytest -n auto


[OK] CI/CD AVEC GITHUB ACTIONS

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

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        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: |
        python -m pip install --upgrade pip
        pip install -e ".[dev]"
    
    - name: Lint with flake8
      run: |
        flake8 src/ tests/ --count --select=E9,F63,F7,F82 --show-source --statistics
        flake8 src/ tests/ --count --exit-zero --max-complexity=10 --max-line-length=88 --statistics
    
    - name: Check formatting with black
      run: |
        black --check src/ tests/
    
    - name: Type check with mypy
      run: |
        mypy src/
    
    - name: Test with pytest
      run: |
        pytest --cov=mypackage --cov-report=xml
    
    - name: Upload coverage to Codecov
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage.xml
        flags: unittests
        name: codecov-umbrella

# === .github/workflows/publish.yml ===
name: Publish to PyPI

on:
  release:
    types: [published]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v3
    
    - name: Set up Python
      uses: actions/setup-python@v4
      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: Check package
      run: twine check dist/*
    
    - name: Publish to PyPI
      env:
        TWINE_USERNAME: __token__
        TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
      run: twine upload dist/*


[OK] SCRIPTS CLI (ENTRY POINTS)

# === src/mypackage/cli.py ===
import click
from . import __version__

@click.group()
@click.version_option(version=__version__)
def main():
    """MyPackage CLI - Outil en ligne de commande."""
    pass

@main.command()
@click.argument('name')
@click.option('--uppercase', '-u', is_flag=True, help='Mettre en majuscules')
def greet(name, uppercase):
    """Saluer quelqu'un.
    
    Exemple:
        mypackage greet Alice
        mypackage greet Bob --uppercase
    """
    from . import hello
    greeting = hello(name)
    if uppercase:
        greeting = greeting.upper()
    click.echo(greeting)

@main.command()
@click.argument('a', type=float)
@click.argument('b', type=float)
def add(a, b):
    """Additionner deux nombres.
    
    Exemple:
        mypackage add 5 3
    """
    from . import add as add_func
    result = add_func(a, b)
    click.echo(f"Résultat: {result}")

@main.command()
@click.option('--verbose', '-v', is_flag=True, help='Mode verbeux')
def info(verbose):
    """Afficher informations sur le package."""
    click.echo(f"MyPackage version {__version__}")
    if verbose:
        click.echo("Développé par: Votre Nom")
        click.echo("Licence: MIT")

if __name__ == '__main__':
    main()

# === Configuration dans pyproject.toml ===
[project.scripts]
mypackage = "mypackage.cli:main"

# === Utilisation ===
# Après installation:
mypackage --help
mypackage --version
mypackage greet Alice
mypackage greet Bob --uppercase
mypackage add 5 3
mypackage info --verbose


[OK] CHANGELOG

# === CHANGELOG.md ===
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Nouvelle fonctionnalité en développement

## [0.2.0] - 2025-02-15

### Added
- Support Python 3.12
- Nouvelle fonction `calculate()`
- CLI avec commande `info`
- Documentation API complète

### Changed
- Amélioration performances fonction `process()`
- Mise à jour dépendances

### Fixed
- Bug dans gestion des erreurs
- Problème encoding UTF-8

### Deprecated
- Fonction `old_function()` sera supprimée en v1.0

## [0.1.1] - 2025-01-20

### Fixed
- Correction bug critique dans `hello()`
- Fix tests sur Windows

## [0.1.0] - 2025-01-10

### Added
- Première version publique
- Fonctions de base: `hello()`, `add()`
- Tests unitaires
- Documentation Sphinx

[Unreleased]: https://github.com/username/mypackage/compare/v0.2.0...HEAD
[0.2.0]: https://github.com/username/mypackage/compare/v0.1.1...v0.2.0
[0.1.1]: https://github.com/username/mypackage/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/username/mypackage/releases/tag/v0.1.0


[OK] CONTRIBUTING.MD

# === CONTRIBUTING.md ===
# Contributing to MyPackage

Merci de contribuer à MyPackage! [BRAVO]

## Comment contribuer

### Rapporter un bug

1. Vérifier que le bug n'est pas déjà rapporté dans [Issues](https://github.com/username/mypackage/issues)
2. Créer une nouvelle issue avec:
   - Description claire du bug
   - Steps pour reproduire
   - Comportement attendu vs actuel
   - Version Python et OS
   - Stack trace si applicable

### Proposer une fonctionnalité

1. Ouvrir une issue pour discuter la fonctionnalité
2. Attendre feedback avant de commencer le code
3. Référencer l'issue dans la Pull Request

### Soumettre une Pull Request

1. **Fork** le projet
2. **Clone** votre fork:
   ```bash
   git clone https://github.com/votre-username/mypackage.git
   cd mypackage
   ```

3. **Créer une branche**:
   ```bash
   git checkout -b feature/ma-nouvelle-fonctionnalite
   ```

4. **Configurer environnement**:
   ```bash
   python -m venv .venv
   source .venv/bin/activate
   pip install -e ".[dev]"
   ```

5. **Faire vos modifications**

6. **Tests**:
   ```bash
   pytest
   pytest --cov=mypackage
   ```

7. **Formatage**:
   ```bash
   black src/ tests/
   isort src/ tests/
   flake8 src/ tests/
   ```

8. **Commit**:
   ```bash
   git add .
   git commit -m "feat: ajouter nouvelle fonctionnalité"
   ```
   
   Format des commits (Conventional Commits):
   - `feat:` Nouvelle fonctionnalité
   - `fix:` Correction de bug
   - `docs:` Documentation
   - `test:` Tests
   - `refactor:` Refactoring
   - `style:` Formatage
   - `chore:` Maintenance

9. **Push**:
   ```bash
   git push origin feature/ma-nouvelle-fonctionnalite
   ```

10. **Créer Pull Request** sur GitHub

## Standards de code

### Style
- PEP 8 (via black et flake8)
- Line length: 88 caractères (black default)
- Docstrings: Google style
- Type hints obligatoires pour fonctions publiques

### Tests
- Couverture minimale: 80%
- Tests pour toute nouvelle fonctionnalité
- Tests pour tout bug fix

### Documentation
- Docstrings pour toutes fonctions/classes publiques
- Exemples dans docstrings
- Mettre à jour README si nécessaire
- Mettre à jour CHANGELOG.md

## Code de conduite

Soyez respectueux et inclusif. Voir [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md).

## Questions?

Ouvrez une [Discussion](https://github.com/username/mypackage/discussions) ou contactez [@username](https://github.com/username).


[OK] CLASSIFIERS PYPI (CATÉGORISATION)

# === Classifiers courants dans pyproject.toml ===
classifiers = [
    # Statut développement
    "Development Status :: 1 - Planning",
    "Development Status :: 2 - Pre-Alpha",
    "Development Status :: 3 - Alpha",
    "Development Status :: 4 - Beta",
    "Development Status :: 5 - Production/Stable",
    "Development Status :: 6 - Mature",
    "Development Status :: 7 - Inactive",
    
    # Audience
    "Intended Audience :: Developers",
    "Intended Audience :: Education",
    "Intended Audience :: End Users/Desktop",
    "Intended Audience :: Science/Research",
    "Intended Audience :: System Administrators",
    
    # Licence
    "License :: OSI Approved :: MIT License",
    "License :: OSI Approved :: Apache Software License",
    "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
    "License :: OSI Approved :: BSD License",
    
    # Versions Python
    "Programming Language :: 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",
    "Programming Language :: Python :: Implementation :: CPython",
    "Programming Language :: Python :: Implementation :: PyPy",
    
    # OS
    "Operating System :: OS Independent",
    "Operating System :: POSIX",
    "Operating System :: Microsoft :: Windows",
    "Operating System :: MacOS",
    
    # Topics
    "Topic :: Software Development",
    "Topic :: Software Development :: Libraries",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Topic :: Utilities",
    "Topic :: Internet :: WWW/HTTP",
    "Topic :: Scientific/Engineering",
    "Topic :: Text Processing",
    
    # Framework
    "Framework :: Django",
    "Framework :: Flask",
    "Framework :: FastAPI",
    "Framework :: Pytest",
    
    # Typing
    "Typing :: Typed",
    
    # Environnement
    "Environment :: Console",
    "Environment :: Web Environment",
    
    # Langue naturelle
    "Natural Language :: English",
    "Natural Language :: French",
]

# Liste complète:
# https://pypi.org/classifiers/


[OK] BADGES POUR README

# === Badges courants ===
# PyPI version
[![PyPI version](https://badge.fury.io/py/mypackage.svg)](https://pypi.org/project/mypackage/)

# Python versions
[![Python versions](https://img.shields.io/pypi/pyversions/mypackage.svg)](https://pypi.org/project/mypackage/)

# Licence
[![License](https://img.shields.io/pypi/l/mypackage.svg)](https://github.com/username/mypackage/blob/main/LICENSE)

# Downloads
[![Downloads](https://pepy.tech/badge/mypackage)](https://pepy.tech/project/mypackage)

# Tests
[![Tests](https://github.com/username/mypackage/workflows/Tests/badge.svg)](https://github.com/username/mypackage/actions)

# Coverage
[![Coverage](https://codecov.io/gh/username/mypackage/branch/main/graph/badge.svg)](https://codecov.io/gh/username/mypackage)

# Documentation
[![Documentation Status](https://readthedocs.org/projects/mypackage/badge/?version=latest)](https://mypackage.readthedocs.io)

# Code quality
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)

# Pre-commit
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)


[OK] PRE-COMMIT HOOKS

# === .pre-commit-config.yaml ===
repos:
  - 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: check-json
      - id: check-toml
      - id: check-merge-conflict
      - id: debug-statements
      
  - repo: https://github.com/psf/black
    rev: 23.11.0
    hooks:
      - id: black
        language_version: python3.11
        
  - repo: https://github.com/pycqa/isort
    rev: 5.12.0
    hooks:
      - id: isort
        args: ["--profile", "black"]
        
  - repo: https://github.com/pycqa/flake8
    rev: 6.1.0
    hooks:
      - id: flake8
        args: ['--max-line-length=88', '--extend-ignore=E203']
        
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.7.1
    hooks:
      - id: mypy
        additional_dependencies: [types-all]

# === Installation ===
pip install pre-commit
pre-commit install

# === Utilisation ===
# Automatique à chaque commit
git commit -m "mon commit"

# Manuel sur tous les fichiers
pre-commit run --all-files

# Sur fichiers stagés seulement
pre-commit run

# Mettre à jour hooks
pre-commit autoupdate


[OK] MAKEFILE (AUTOMATISATION)

# === Makefile ===
.PHONY: help install dev test lint format clean build publish docs

help:  ## Afficher cette aide
	@grep -E '^[a-zA-Z_-]+:.*?## .*$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $1, $2}'

install:  ## Installer le package
	pip install -e .

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

test:  ## Lancer les tests
	pytest

test-cov:  ## Tests avec couverture
	pytest --cov=mypackage --cov-report=html --cov-report=term

lint:  ## Vérifier le code (flake8, mypy)
	flake8 src/ tests/
	mypy src/

format:  ## Formater le code (black, isort)
	black src/ tests/
	isort src/ tests/

format-check:  ## Vérifier formatage sans modifier
	black --check src/ tests/
	isort --check-only src/ tests/

clean:  ## Nettoyer fichiers temporaires
	rm -rf build/
	rm -rf dist/
	rm -rf *.egg-info
	rm -rf .pytest_cache/
	rm -rf .mypy_cache/
	rm -rf htmlcov/
	rm -rf .coverage
	find . -type d -name __pycache__ -exec rm -rf {} +
	find . -type f -name "*.pyc" -delete

build:  ## Builder le package
	python -m build

publish-test:  ## Publier sur TestPyPI
	twine upload --repository testpypi dist/*

publish:  ## Publier sur PyPI
	twine upload dist/*

docs:  ## Générer documentation
	cd docs && make html

docs-serve:  ## Servir documentation localement
	cd docs/build/html && python -m http.server

all: clean format lint test build  ## Tout faire

# === Utilisation ===
make help           # Voir toutes les commandes
make dev            # Setup développement
make test           # Lancer tests
make format         # Formater code
make build          # Builder package
make publish        # Publier sur PyPI


[OK] TOX (TESTS MULTI-ENVIRONNEMENTS)

# === tox.ini ===
[tox]
envlist = py38,py39,py310,py311,py312,lint,docs
isolated_build = True

[testenv]
deps =
    pytest>=7.0.0
    pytest-cov>=4.0.0
commands =
    pytest --cov=mypackage --cov-report=term-missing

[testenv:lint]
deps =
    black
    flake8
    isort
    mypy
commands =
    black --check src/ tests/
    isort --check-only src/ tests/
    flake8 src/ tests/
    mypy src/

[testenv:docs]
changedir = docs
deps =
    sphinx
    sphinx-rtd-theme
commands =
    sphinx-build -W -b html source build/html

[testenv:format]
deps =
    black
    isort
commands =
    black src/ tests/
    isort src/ tests/

# === Installation et utilisation ===
pip install tox

# Lancer tous les environnements
tox

# Environnement spécifique
tox -e py311
tox -e lint
tox -e docs

# Parallélisation
tox -p auto


[OK] TYPE HINTS (ANNOTATIONS DE TYPES)

# === Annotations basiques ===
from typing import List, Dict, Tuple, Set, Optional, Union, Any

def greet(name: str) -> str:
    """Salue une personne."""
    return f"Bonjour {name}!"

def add(a: int, b: int) -> int:
    """Additionne deux entiers."""
    return a + b

def process_list(items: List[str]) -> List[str]:
    """Traite une liste de chaînes."""
    return [item.upper() for item in items]

def get_config() -> Dict[str, Any]:
    """Retourne configuration."""
    return {"timeout": 30, "retries": 3}

# === Types optionnels ===
def find_user(user_id: int) -> Optional[Dict[str, str]]:
    """Trouve un utilisateur ou None."""
    # Peut retourner Dict ou None
    if user_id > 0:
        return {"name": "Alice", "email": "alice@example.com"}
    return None

# === Union de types ===
def process(data: Union[str, int, float]) -> str:
    """Accepte plusieurs types."""
    return str(data)

# === Types génériques ===
from typing import TypeVar, Generic

T = TypeVar('T')

def first(items: List[T]) -> Optional[T]:
    """Retourne premier élément ou None."""
    return items[0] if items else None

class Container(Generic[T]):
    """Container générique."""
    def __init__(self, value: T) -> None:
        self.value = value
    
    def get(self) -> T:
        return self.value

# === Callable ===
from typing import Callable

def apply(func: Callable[[int, int], int], a: int, b: int) -> int:
    """Applique une fonction."""
    return func(a, b)

# === Protocol (Duck Typing) ===
from typing import Protocol

class Drawable(Protocol):
    """Protocol pour objets dessinables."""
    def draw(self) -> None: ...

def render(obj: Drawable) -> None:
    """Rend un objet dessinable."""
    obj.draw()

# === Python 3.10+ (Union avec |) ===
def process_new(data: str | int | float) -> str:
    """Syntaxe moderne pour Union."""
    return str(data)

def find_new(user_id: int) -> dict[str, str] | None:
    """Syntaxe moderne pour Optional."""
    return {"name": "Alice"} if user_id > 0 else None


[OK] STRUCTURE COMPLÈTE D'UN PROJET PROFESSIONNEL

myproject/
├── .github/
│   ├── workflows/
│   │   ├── tests.yml
│   │   ├── publish.yml
│   │   └── docs.yml
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── pull_request_template.md
│
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── _version.py
│       ├── core/
│       │   ├── __init__.py
│       │   ├── engine.py
│       │   └── processor.py
│       ├── utils/
│       │   ├── __init__.py
│       │   ├── helpers.py
│       │   └── validators.py
│       ├── api/
│       │   ├── __init__.py
│       │   ├── client.py
│       │   └── exceptions.py
│       ├── cli.py
│       ├── config.py
│       └── py.typed              # Pour type hints
│
├── tests/
│   ├── __init__.py
│   ├── conftest.py
│   ├── unit/
│   │   ├── test_core.py
│   │   └── test_utils.py
│   ├── integration/
│   │   └── test_integration.py
│   └── fixtures/
│       └── data.json
│
├── docs/
│   ├── source/
│   │   ├── conf.py
│   │   ├── index.rst
│   │   ├── installation.rst
│   │   ├── quickstart.rst
│   │   ├── api.rst
│   │   ├── examples.rst
│   │   └── _static/
│   ├── build/
│   └── Makefile
│
├── examples/
│   ├── basic_usage.py
│   └── advanced_usage.py
│
├── scripts/
│   ├── setup_dev.sh
│   └── release.sh
│
├── .gitignore
├── .pre-commit-config.yaml
├── .readthedocs.yaml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── MANIFEST.in
├── README.md
├── pyproject.toml
├── requirements.txt
├── requirements-dev.txt
├── setup.py                     # Optionnel pour compatibilité
├── setup.cfg                    # Optionnel
└── tox.ini


[OK] EXEMPLES DE MODULES RÉELS À ÉTUDIER

# === Modules simples et bien structurés ===
# requests
# https://github.com/psf/requests
# - Structure claire
# - Documentation excellente
# - Tests complets

# click
# https://github.com/pallets/click
# - CLI framework
# - Documentation superbe
# - Exemples nombreux

# rich
# https://github.com/Textualize/rich
# - Moderne
# - Type hints complets
# - Docs magnifiques

# === Modules avec bonne CI/CD ===
# httpx
# https://github.com/encode/httpx
# - Tests exhaustifs
# - GitHub Actions moderne
# - Coverage excellent

# pydantic
# https://github.com/pydantic/pydantic
# - Type hints avancés
# - Documentation Read the Docs
# - Tests rigoureux


[OK] OUTILS DE QUALITÉ DE CODE

# === Black (formatage) ===
pip install black
black src/ tests/
black --check src/          # Vérifier sans modifier
black --diff src/           # Voir les changements

# Configuration dans pyproject.toml
[tool.black]
line-length = 88
target-version = ["py38"]

# === isort (imports) ===
pip install isort
isort src/ tests/
isort --check-only src/     # Vérifier

# Configuration
[tool.isort]
profile = "black"
line_length = 88

# === flake8 (linting) ===
pip install flake8
flake8 src/ tests/

# Configuration dans .flake8 ou setup.cfg
[flake8]
max-line-length = 88
extend-ignore = E203, W503
exclude = .git,__pycache__,build,dist

# === pylint (analyse approfondie) ===
pip install pylint
pylint src/

# Configuration dans .pylintrc
[MASTER]
max-line-length = 88

[MESSAGES CONTROL]
disable = C0111, C0103

# === mypy (type checking) ===
pip install mypy
mypy src/

# Configuration
[tool.mypy]
python_version = "3.8"
warn_return_any = true
strict_optional = true
disallow_untyped_defs = true

# === bandit (sécurité) ===
pip install bandit
bandit -r src/

# === safety (vulnérabilités dépendances) ===
pip install safety
safety check
safety check --json

# === radon (complexité) ===
pip install radon
radon cc src/              # Cyclomatic complexity
radon mi src/              # Maintainability index

# === vulture (code mort) ===
pip install vulture
vulture src/


[OK] PATTERNS DE DÉVELOPPEMENT

# === Singleton ===
class Singleton:
    """Pattern Singleton."""
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

# === Factory ===
class AnimalFactory:
    """Pattern Factory."""
    @staticmethod
    def create(animal_type: str):
        if animal_type == "dog":
            return Dog()
        elif animal_type == "cat":
            return Cat()
        raise ValueError(f"Type inconnu: {animal_type}")

# === Builder ===
class QueryBuilder:
    """Pattern Builder."""
    def __init__(self):
        self._query = []
    
    def select(self, fields: str):
        self._query.append(f"SELECT {fields}")
        return self
    
    def from_table(self, table: str):
        self._query.append(f"FROM {table}")
        return self
    
    def where(self, condition: str):
        self._query.append(f"WHERE {condition}")
        return self
    
    def build(self) -> str:
        return " ".join(self._query)

# Utilisation:
query = QueryBuilder().select("*").from_table("users").where("age > 18").build()

# === Context Manager ===
class FileManager:
    """Pattern Context Manager."""
    def __init__(self, filename: str, mode: str = 'r'):
        self.filename = filename
        self.mode = mode
        self.file = None
    
    def __enter__(self):
        self.file = open(self.filename, self.mode)
        return self.file
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.file:
            self.file.close()

# Utilisation:
with FileManager('data.txt', 'r') as f:
    content = f.read()


[OK] GESTION DES CONFIGURATIONS

# === config.py ===
from typing import Dict, Any
from pathlib import Path
import os

class Config:
    """Configuration du package."""
    
    # Valeurs par défaut
    DEFAULT_TIMEOUT = 30
    DEFAULT_RETRIES = 3
    DEFAULT_BASE_URL = "https://api.example.com"
    
    def __init__(self):
        self.timeout = int(os.getenv("MYPACKAGE_TIMEOUT", self.DEFAULT_TIMEOUT))
        self.retries = int(os.getenv("MYPACKAGE_RETRIES", self.DEFAULT_RETRIES))
        self.base_url = os.getenv("MYPACKAGE_BASE_URL", self.DEFAULT_BASE_URL)
    
    @classmethod
    def from_dict(cls, config: Dict[str, Any]) -> 'Config':
        """Créer config depuis dictionnaire."""
        instance = cls()
        for key, value in config.items():
            if hasattr(instance, key):
                setattr(instance, key, value)
        return instance
    
    @classmethod
    def from_file(cls, filepath: Path) -> 'Config':
        """Charger config depuis fichier."""
        import json
        with open(filepath) as f:
            data = json.load(f)
        return cls.from_dict(data)
    
    def to_dict(self) -> Dict[str, Any]:
        """Convertir en dictionnaire."""
        return {
            "timeout": self.timeout,
            "retries": self.retries,
            "base_url": self.base_url,
        }

# Singleton pour config globale
_config = None

def get_config() -> Config:
    """Obtenir configuration globale."""
    global _config
    if _config is None:
        _config = Config()
    return _config

def set_config(config: Config) -> None:
    """Définir configuration globale."""
    global _config
    _config = config


[OK] LOGGING

# === logging_config.py ===
import logging
import sys
from pathlib import Path

def setup_logging(
    level: int = logging.INFO,
    log_file: Path = None,
    format_string: str = None
) -> logging.Logger:
    """Configure le logging pour le package.
    
    Args:
        level: Niveau de logging
        log_file: Fichier de log (optionnel)
        format_string: Format personnalisé (optionnel)
    
    Returns:
        Logger configuré
    """
    if format_string is None:
        format_string = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    
    # Formatter
    formatter = logging.Formatter(format_string)
    
    # Logger principal
    logger = logging.getLogger("mypackage")
    logger.setLevel(level)
    
    # Handler console
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)
    
    # Handler fichier si spécifié
    if log_file:
        file_handler = logging.FileHandler(log_file)
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)
    
    return logger

# === Utilisation dans le code ===
import logging

logger = logging.getLogger("mypackage")

def my_function():
    logger.debug("Message de debug")
    logger.info("Information")
    logger.warning("Avertissement")
    logger.error("Erreur")
    logger.critical("Critique")


[OK] EXCEPTIONS PERSONNALISÉES

# === exceptions.py ===
class MyPackageError(Exception):
    """Erreur de base pour toutes les exceptions du package."""
    pass

class ValidationError(MyPackageError):
    """Erreur de validation."""
    def __init__(self, field: str, message: str):
        self.field = field
        self.message = message
        super().__init__(f"Validation error on '{field}': {message}")

class APIError(MyPackageError):
    """Erreur API."""
    def __init__(self, status_code: int, message: str):
        self.status_code = status_code
        self.message = message
        super().__init__(f"API Error {status_code}: {message}")

class ConfigurationError(MyPackageError):
    """Erreur de configuration."""
    pass

class NotFoundError(MyPackageError):
    """Ressource non trouvée."""
    def __init__(self, resource: str, identifier: str):
        self.resource = resource
        self.identifier = identifier
        super().__init__(f"{resource} '{identifier}' not found")

# === Utilisation ===
from mypackage.exceptions import ValidationError, NotFoundError

def validate_email(email: str):
    if "@" not in email:
        raise ValidationError("email", "Email must contain @")

def get_user(user_id: int):
    user = find_user(user_id)
    if user is None:
        raise NotFoundError("User", str(user_id))
    return user


[OK] PACKAGING AVANCÉ

# === Inclure fichiers de données ===
# MANIFEST.in
include src/mypackage/data/*.json
include src/mypackage/templates/*.html
recursive-include src/mypackage/static *

# pyproject.toml
[tool.setuptools.package-data]
mypackage = [
    "data/*.json",
    "templates/*.html",
    "static/**/*",
]

# === Accéder aux fichiers de données ===
from importlib.resources import files
import json

def load_config():
    """Charge config depuis package data."""
    config_file = files("mypackage.data").joinpath("config.json")
    with config_file.open() as f:
        return json.load(f)

# Ancienne méthode (< Python 3.9)
from pkg_resources import resource_filename
config_path = resource_filename("mypackage", "data/config.json")

# === Extensions C (Cython, C++) ===
# setup.py avec extension C
from setuptools import setup, Extension

ext_modules = [
    Extension(
        "mypackage.fast_module",
        sources=["src/mypackage/fast_module.c"],
        include_dirs=["src/mypackage/include"],
    )
]

setup(
    name="mypackage",
    ext_modules=ext_modules,
)

# === Wheels spécifiques à la plateforme ===
# Pour extensions C/Cython
python -m build --wheel

# Créer wheels pour plusieurs plateformes avec cibuildwheel
pip install cibuildwheel
cibuildwheel --platform linux


[OK] VERSIONING AUTOMATIQUE

# === setuptools-scm (depuis Git tags) ===
# pyproject.toml
[build-system]
requires = ["setuptools>=61.0", "setuptools-scm"]
build-backend = "setuptools.build_meta"

[tool.setuptools_scm]
write_to = "src/mypackage/_version.py"

# src/mypackage/__init__.py
from ._version import version as __version__

# Créer tags pour versions
git tag v0.1.0
git push origin v0.1.0

# === bump2version ===
pip install bump2version

# .bumpversion.cfg
[bumpversion]
current_version = 0.1.0
commit = True
tag = True

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

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

# Utilisation
bump2version patch  # 0.1.0 -> 0.1.1
bump2version minor  # 0.1.1 -> 0.2.0
bump2version major  # 0.2.0 -> 1.0.0


[OK] PROFILING ET OPTIMISATION

# === cProfile (profiling) ===
import cProfile
import pstats

# Profiler une fonction
cProfile.run('my_function()', 'output.prof')

# Analyser résultats
p = pstats.Stats('output.prof')
p.sort_stats('cumulative')
p.print_stats(10)  # Top 10

# === line_profiler (ligne par ligne) ===
pip install line_profiler

# Décorer fonction à profiler
@profile
def my_function():
    # code

# Exécuter
kernprof -l -v script.py

# === memory_profiler ===
pip install memory_profiler

@profile
def my_function():
    # code

python -m memory_profiler script.py

# === timeit (mesurer temps) ===
import timeit

time = timeit.timeit(
    'my_function()',
    setup='from mymodule import my_function',
    number=1000
)
print(f"Temps moyen: {time/1000:.6f}s")


[OK] SÉCURITÉ

# === Vérifier vulnérabilités ===
# safety
pip install safety
safety check
safety check --json > security-report.json

# bandit (analyse code)
pip install bandit
bandit -r src/
bandit -r src/ -f json -o bandit-report.json

# === Secrets dans le code ===
# detect-secrets
pip install detect-secrets
detect-secrets scan > .secrets.baseline
detect-secrets audit .secrets.baseline

# === Requirements avec hashes ===
pip freeze --all > requirements.txt
pip-compile --generate-hashes requirements.in

# Installer avec vérification
pip install --require-hashes -r requirements.txt


[OK] INTERNATIONALISATION (I18N)

# === babel pour traductions ===
pip install Babel

# pyproject.toml
[tool.babel.extract]
mapping_file = "babel.cfg"
output_file = "locale/messages.pot"

# babel.cfg
[python: **.py]
[jinja2: **/templates/**.html]

# Extraire messages à traduire
pybabel extract -F babel.cfg -o locale/messages.pot src/

# Créer catalogue pour langue
pybabel init -i locale/messages.pot -d locale -l fr
pybabel init -i locale/messages.pot -d locale -l es

# Compiler traductions
pybabel compile -d locale

# === Utilisation dans le code ===
from babel.support import Translations

translations = Translations.load('locale', ['fr'])
_ = translations.gettext

print(_("Hello"))  # Affiche traduction française


[OK] WORKFLOW COMPLET DE PUBLICATION

# === 1. Développement ===
# Créer branche
git checkout -b feature/nouvelle-fonctionnalite

# Développer et tester
# ... code ...
pytest

# Commit
git add .
git commit -m "feat: ajouter nouvelle fonctionnalité"

# === 2. Pull Request ===
git push origin feature/nouvelle-fonctionnalite
# Créer PR sur GitHub
# Attendre revue et CI verts

# === 3. Merge ===
# Merger dans main
git checkout main
git pull origin main

# === 4. Préparer release ===
# Mettre à jour CHANGELOG.md
# Mettre à jour version
bump2version minor  # 0.1.0 -> 0.2.0

# Ou manuellement dans pyproject.toml et __init__.py

# === 5. Tag et push ===
git tag -a v0.2.0 -m "Release version 0.2.0"
git push origin main --tags

# === 6. Build ===
# Nettoyer
rm -rf dist/ build/ *.egg-info

# Builder
python -m build

# Vérifier
twine check dist/*

# === 7. Test sur TestPyPI ===
twine upload --repository testpypi dist/*

# Tester installation
pip install --index-url https://test.pypi.org/simple/ mypackage==0.2.0

# === 8. Publier sur PyPI ===
twine upload dist/*

# === 9. Vérifier ===
# Visiter https://pypi.org/project/mypackage/
pip install --upgrade mypackage

# === 10. GitHub Release ===
# Créer release sur GitHub avec notes de CHANGELOG
# Attacher les fichiers dist/* si souhaité

# === 11. Annoncer ===
# Twitter, Reddit, blog, etc.


[OK] CHECKLIST AVANT PUBLICATION

[WHITE_SQUARE] Tests passent (pytest)
[WHITE_SQUARE] Couverture ≥ 80%
[WHITE_SQUARE] Linting OK (flake8, pylint)
[WHITE_SQUARE] Type checking OK (mypy)
[WHITE_SQUARE] Formatage OK (black, isort)
[WHITE_SQUARE] Documentation complète
  [WHITE_SQUARE] Docstrings toutes fonctions publiques
  [WHITE_SQUARE] README.md à jour
  [WHITE_SQUARE] CHANGELOG.md à jour
  [WHITE_SQUARE] Documentation Sphinx générée
[WHITE_SQUARE] Fichiers obligatoires présents
  [WHITE_SQUARE] LICENSE
  [WHITE_SQUARE] README.md
  [WHITE_SQUARE] pyproject.toml ou setup.py
  [WHITE_SQUARE] CHANGELOG.md
  [WHITE_SQUARE] CONTRIBUTING.md (recommandé)
[WHITE_SQUARE] Version mise à jour
  [WHITE_SQUARE] pyproject.toml
  [WHITE_SQUARE] __init__.py
  [WHITE_SQUARE] CHANGELOG.md
[WHITE_SQUARE] Git propre
  [WHITE_SQUARE] Tout commité
  [WHITE_SQUARE] Tag créé
  [WHITE_SQUARE] Push origin main --tags
[WHITE_SQUARE] Build OK
  [WHITE_SQUARE] python -m build sans erreur
  [WHITE_SQUARE] twine check dist/* OK
[WHITE_SQUARE] Testé sur TestPyPI
[WHITE_SQUARE] CI/CD vert
[WHITE_SQUARE] Read the Docs build OK


[OK] RESSOURCES ET DOCUMENTATION

# === Documentation officielle ===
# Packaging Python
https://packaging.python.org/

# PyPI
https://pypi.org/

# setuptools
https://setuptools.pypa.io/

# Sphinx
https://www.sphinx-doc.org/

# Read the Docs
https://docs.readthedocs.io/

# === Guides et tutoriels ===
# Packaging Guide officiel
https://packaging.python.org/tutorials/packaging-projects/

# Real Python - Publishing
https://realpython.com/pypi-publish-python-package/

# Python Packaging User Guide
https://packaging.python.org/guides/

# === Outils ===
# PyPI Classifier List
https://pypi.org/classifiers/

# Choose a License
https://choosealicense.com/

# Keep a Changelog
https://keepachangelog.com/

# Semantic Versioning
https://semver.org/

# Conventional Commits
https://www.conventionalcommits.org/

# === Exemples de bons projets ===
# cookiecutter-pypackage (template)
https://github.com/audreyfeldroy/cookiecutter-pypackage

# Poetry
https://python-poetry.org/

# Requests
https://github.com/psf/requests

# Click
https://github.com/pallets/click


[OK] ERREURS COURANTES ET SOLUTIONS

# === Erreur: Module not found après installation ===
# Cause: Mauvaise structure ou package-dir incorrect
# Solution: Vérifier structure et pyproject.toml

[tool.setuptools.packages.find]
where = ["src"]
include = ["mypackage*"]

# === Erreur: Version conflict avec dépendances ===
# Cause: Versions trop strictes
# Solution: Utiliser ranges flexibles

dependencies = [
    "requests>=2.28.0,<3.0.0",  # Flexible
    # pas "requests==2.28.0"     # Trop strict
]

# === Erreur: Import Error in tests ===
# Cause: Package pas installé en mode éditable
# Solution:
pip install -e .

# === Erreur: Twine upload failed ===
# Cause: Version déjà existante ou credentials invalides
# Solutions:
# 1. Incrémenter version
# 2. Vérifier token PyPI
# 3. Supprimer et rebuilder dist/

# === Erreur: Read the Docs build failed ===
# Cause: Dépendances manquantes ou conf.py incorrect
# Solutions:
# 1. Vérifier .readthedocs.yaml
# 2. Ajouter dépendances docs dans [project.optional-dependencies]
# 3. Vérifier sys.path dans conf.py

# === Erreur: GitHub Actions failed ===
# Cause: Secrets manquants ou workflow incorrect
# Solutions:
# 1. Ajouter PYPI_API_TOKEN dans GitHub Secrets
# 2. Vérifier syntaxe YAML
# 3. Tester localement avec act

# === Erreur: Package data not included ===
# Cause: MANIFEST.in ou package-data incorrect
# Solutions:
# 1. Vérifier MANIFEST.in
# 2. Ajouter include_package_data = True
# 3. Lister contenu wheel: unzip -l dist/*.whl