# Fichier: python_cheats/cheatsheets/httpx.txt
# Cheatsheet HTTPX - Guide Ultra-Détaillé pour Grands Débutants


[OK] CONCEPTS FONDAMENTAUX (EXPLICATIONS TRÈS DÉTAILLÉES)

# === QU'EST-CE QUE HTTPX? ===

# Imagine que tu veux télécharger une page web ou appeler une API
# Exemple: Tu veux récupérer la météo d'une API

# Solution classique (sans httpx):
# C'est compliqué! Tu dois gérer les connexions réseau, les erreurs, etc.

# HTTPX = Bibliothèque Python qui fait ça pour toi!
# Tu dis juste: "Envoie une requête HTTP à cette URL"
# Et httpx gère TOUT

# httpx s'appelle un "HTTP Client"
# = Un outil pour communiquer avec des serveurs web


# === POURQUOI HTTPX AU LIEU DE REQUESTS? ===

# REQUESTS = Ancienne biblio (créée en 2010)
# - Synchrone (bloquant)
# - Pas de support HTTP/2

# HTTPX = Nouvelle biblio (créée en 2019)
# - Synchrone ET asynchrone (non-bloquant)
# - Support HTTP/2 et HTTP/1.1
# - Plus rapide
# - API moderne et cohérente
# - Mieux maintenue

# Analogie:
# REQUESTS = Voiture essence (ancienne)
# HTTPX = Voiture électrique (nouvelle, meilleure)

# Pour un débutant: HTTPX est plus facile à apprendre


# === VOCABULAIRE HTTPX (TRÈS IMPORTANT!) ===

# REQUEST (Requête)
# = Un message qu'on ENVOIE au serveur
# = "Donne-moi la page https://example.com"
# = Contient: URL, méthode (GET/POST/etc), headers, data

# RESPONSE (Réponse)
# = Un message qu'on REÇOIT du serveur
# = Contient: le contenu de la page, code d'erreur (200, 404, etc), headers

# HTTP METHOD (Méthode HTTP)
# = Type d'action qu'on demande au serveur
# Principaux types:
#   - GET: Récupérer des données (lire)
#   - POST: Envoyer des données (créer)
#   - PUT: Remplacer des données (modifier complètement)
#   - PATCH: Modifier partiellement des données
#   - DELETE: Supprimer des données
# Analogie: Comme des opérations sur une base de données!

# STATUS CODE (Code de statut)
# = Nombre qui dit si la requête a réussi
# Principaux codes:
#   - 200: OK! Succès
#   - 201: Créé! (POST a créé quelque chose)
#   - 400: Erreur du client (mauvaise requête)
#   - 401: Non autorisé (auth échouée)
#   - 404: Non trouvé (page existe pas)
#   - 500: Erreur serveur (le serveur a un problème)

# ENDPOINT (Point de terminaison)
# = Une URL spécifique d'une API
# Exemple: https://api.example.com/users
# = L'endpoint pour récupérer les utilisateurs

# HEADER (En-tête)
# = Infos supplémentaires dans la requête/réponse
# Exemples:
#   - Content-Type: application/json (dit qu'on envoie du JSON)
#   - Authorization: Bearer token (infos d'authentification)
#   - User-Agent: httpx/0.23.0 (identifie le client)

# BODY (Corps)
# = Les données que tu envoies/reçois
# Formats:
#   - Texte plain
#   - JSON
#   - Formulaire
#   - Binaire (images, fichiers, etc)

# TIMEOUT (Délai d'attente)
# = Combien de temps tu attends une réponse avant d'abandonner
# Par défaut: httpx attend 30 secondes
# = Si pas de réponse après 30s, erreur TimeoutException

# COOKIE (Cookie)
# = Petit fichier texte stocké sur ton ordinateur
# = Identifie l'utilisateur (pour les sessions, logins, etc)
# Exemple: Quand tu visitesamazon.com, Amazon te donne un cookie
# = Prochaine visite, tu es toujours "connecté"

# SESSION (Session)
# = Objet qu'on réutilise pour plusieurs requêtes
# = Mémorise les cookies, les headers par défaut, etc
# = Plus efficace que créer une nouvelle connexion chaque fois


# === COMMENT ÇA MARCHE? (FLUX COMPLET) ===

# 1. Tu crées une requête HTTP
#    Exemple: "GET https://api.example.com/users"

# 2. Tu envoies la requête au serveur
#    httpx établit une connexion TCP/IP
#    Envoie les données

# 3. Le serveur reçoit et traite
#    Cherche la ressource demandée
#    Prépare une réponse

# 4. Le serveur envoie la réponse
#    Code de statut (200, 404, etc)
#    Headers
#    Corps (données)

# 5. Tu reçois la réponse dans httpx
#    httpx la parse (interprète)
#    Tu accèdes aux données en Python

# 6. Ton code traite la réponse
#    Affiche, sauvegarde, traite les données, etc


[OK] INSTALLATION SUPER DÉTAILLÉE

# === PRÉREQUIS ===

# Python 3.7+ installé
# pip disponible

# Vérifier:
python --version
# Doit afficher: Python 3.X.X (X >= 7)

pip --version
# Doit afficher: pip X.X.X


# === ÉTAPE 1: INSTALLER HTTPX ===

# Commande simple:
pip install httpx

# Affiche:
# Collecting httpx
# Downloading httpx-0.23.0-py3-none-any.whl
# Installing collected packages: httpx
# Successfully installed httpx-0.23.0

# Vérifier l'installation:
python -c "import httpx; print(httpx.__version__)"
# Doit afficher: 0.23.0 ou plus


# === ÉTAPE 2: CRÉER UN ENVIRONNEMENT VIRTUEL (RECOMMANDÉ) ===

# Pourquoi? Isoler les packages pour ce projet

# Créer:
python -m venv venv

# Activer:
source venv/bin/activate  # Linux/macOS
venv\Scripts\activate     # Windows

# Doit afficher: (venv) $ ou (venv) C:\...

# Installer httpx dans le venv:
pip install httpx

# Vérifier:
python -c "import httpx; print('httpx installé!')"
# Affiche: httpx installé!


[OK] USAGE BASIQUE (LE CŒUR DE HTTPX)

# === REQUÊTE GET SIMPLE ===

# Récupérer le contenu d'une page web

import httpx

# Créer une requête GET
response = httpx.get("https://example.com")

# Afficher le contenu
print(response.text)
# Affiche: "<!DOCTYPE html>..." (le code HTML de la page)

# Afficher le code de statut
print(response.status_code)
# Affiche: 200 (succès!)

# Explications:
# httpx.get() = Envoie une requête GET à l'URL
# response = Objet Response contenant la réponse du serveur
# response.text = Le corps de la réponse en tant que texte
# response.status_code = Le code de statut (200, 404, etc)


# === REQUÊTE GET AVEC PARAMÈTRES ===

# Passer des paramètres dans l'URL

response = httpx.get(
    "https://api.example.com/search",
    params={
        "q": "python",
        "limit": 10
    }
)

# httpx construit l'URL automatiquement:
# https://api.example.com/search?q=python&limit=10

# Afficher la URL finale:
print(response.url)
# Affiche: https://api.example.com/search?q=python&limit=10

# Explications:
# params = Dictionnaire des paramètres de recherche
# httpx encode et ajoute les paramètres à l'URL automatiquement
# Plus sûr que de les ajouter manuellement


# === REQUÊTE GET AVEC HEADERS ===

# Ajouter des en-têtes personnalisés

response = httpx.get(
    "https://api.example.com/data",
    headers={
        "Authorization": "Bearer my_secret_token",
        "User-Agent": "MyApp/1.0"
    }
)

# Afficher les headers reçus:
print(response.headers)
# Affiche: {'content-type': 'application/json', ...}

# Accéder à un header spécifique:
print(response.headers['content-type'])
# Affiche: application/json

# Explications:
# headers = Dictionnaire des en-têtes HTTP
# Utilisé pour l'authentification, de dire au serveur qui on est, etc
# response.headers = Les headers que le serveur a renvoyés


# === REQUÊTE POST SIMPLE ===

# Envoyer des données au serveur (créer quelque chose)

response = httpx.post(
    "https://api.example.com/users",
    json={
        "name": "Jean",
        "email": "jean@example.com",
        "age": 30
    }
)

# Afficher la réponse
print(response.json())
# Affiche: {'id': 123, 'name': 'Jean', ...}

# Vérifier que la création a réussi:
if response.status_code == 201:
    print("Utilisateur créé!")
else:
    print(f"Erreur: {response.status_code}")

# Explications:
# httpx.post() = Envoie une requête POST
# json= = Convertit le dictionnaire en JSON automatiquement
# response.json() = Parse la réponse JSON et retourne un dictionnaire
# Status code 201 = "Créé" (création réussie)


# === REQUÊTE POST AVEC FORMULAIRE ===

# Envoyer des données de formulaire (pas JSON)

response = httpx.post(
    "https://example.com/login",
    data={
        "username": "jean",
        "password": "secret123"
    }
)

# Explications:
# data= = Envoie les données sous forme de formulaire (application/x-www-form-urlencoded)
# Utilisé pour les login, formulaires HTML, etc


# === REQUÊTE POST AVEC FICHIER ===

# Envoyer un fichier au serveur

response = httpx.post(
    "https://api.example.com/upload",
    files={
        "file": open("document.pdf", "rb")
    }
)

# Ou avec un contexte manager (ferme auto le fichier):
with open("document.pdf", "rb") as f:
    response = httpx.post(
        "https://api.example.com/upload",
        files={"file": f}
    )

# Afficher la réponse
print(response.json())
# Affiche: {'message': 'Fichier téléchargé!', 'id': 456}

# Explications:
# files= = Envoie un fichier multipart/form-data
# "rb" = Read Binary (lire en mode binaire)
# open(...).close() n'est pas appelé avec "with" (context manager s'en charge)


# === REQUÊTE PUT (Remplacer) ===

# Remplacer complètement une ressource

response = httpx.put(
    "https://api.example.com/users/123",
    json={
        "name": "Jean Dupont",
        "email": "jean.dupont@example.com",
        "age": 31
    }
)

# Afficher la réponse
print(response.json())
# Affiche: {'id': 123, 'name': 'Jean Dupont', ...}

# Explications:
# PUT remplace la TOTALITÉ de la ressource
# Tous les champs doivent être fournis


# === REQUÊTE PATCH (Modifier partiellement) ===

# Modifier seulement quelques champs

response = httpx.patch(
    "https://api.example.com/users/123",
    json={
        "age": 32
    }
)

# Afficher la réponse
print(response.json())
# Affiche: {'id': 123, 'name': 'Jean Dupont', 'age': 32, ...}

# Explications:
# PATCH modifie PARTIELLEMENT la ressource
# Les champs non fournis restent inchangés


# === REQUÊTE DELETE ===

# Supprimer une ressource

response = httpx.delete("https://api.example.com/users/123")

# Vérifier le résultat
if response.status_code == 204:
    print("Utilisateur supprimé!")
else:
    print(f"Erreur: {response.status_code}")

# Explications:
# DELETE supprime la ressource
# Status code 204 = "No Content" (suppression réussie, pas de contenu à retourner)


# === GÉRER LES ERREURS ===

# Une requête peut échouer pour plusieurs raisons

try:
    response = httpx.get("https://api.example.com/data", timeout=5)
    
    # Vérifier le code de statut
    response.raise_for_status()
    # Lève une HTTPStatusError si status code >= 400
    
    # Utiliser la réponse
    print(response.json())

except httpx.TimeoutException:
    print("Timeout: Pas de réponse après 5 secondes")

except httpx.HTTPStatusError as e:
    print(f"Erreur HTTP: {e.response.status_code}")
    print(f"Contenu: {e.response.text}")

except httpx.RequestError as e:
    print(f"Erreur de requête: {e}")

# Explications:
# raise_for_status() = Lève une exception si status code >= 400
# TimeoutException = La requête a pris trop de temps
# HTTPStatusError = Le serveur a retourné une erreur
# RequestError = Problème réseau ou autre


[OK] OBJETS DE RÉPONSE (COMMENT ACCÉDER AUX DONNÉES)

# === PROPRIÉTÉS DE RESPONSE ===

response = httpx.get("https://api.example.com/user")

# response.status_code
# = Code de statut HTTP (200, 404, 500, etc)
print(response.status_code)
# Affiche: 200

# response.text
# = Contenu en tant que texte (string)
print(response.text)
# Affiche: "{'name': 'Jean', ...}" (texte brut)

# response.content
# = Contenu en tant que bytes (binaire)
print(response.content)
# Affiche: b"{'name': 'Jean', ...}"

# response.json()
# = Parse le contenu JSON et retourne un dictionnaire/liste
data = response.json()
print(data['name'])
# Affiche: Jean

# response.url
# = URL finale (après redirections)
print(response.url)
# Affiche: https://api.example.com/user

# response.headers
# = Dictionnaire des en-têtes de réponse
print(response.headers)
# Affiche: {'content-type': 'application/json', ...}

# response.headers['content-type']
# = Accéder à un header spécifique
print(response.headers['content-type'])
# Affiche: application/json

# response.cookies
# = Dictionnaire des cookies reçus
print(response.cookies)
# Affiche: {'session_id': 'abc123def456', ...}

# response.is_success
# = True si status code 200-299, False sinon
if response.is_success:
    print("Succès!")

# response.is_redirect
# = True si status code 300-399 (redirection)
if response.is_redirect:
    print("Redirection vers:", response.headers['location'])

# response.is_error
# = True si status code >= 400 (erreur)
if response.is_error:
    print("Erreur!")

# response.reason_phrase
# = Message en texte du status code
print(response.reason_phrase)
# Affiche: OK (pour 200)

# response.elapsed
# = Temps écoulé pour la requête
print(response.elapsed)
# Affiche: 0:00:0.123456 (123 millisecondes)


[OK] SESSIONS (RÉUTILISER DES CONNEXIONS)

# === POURQUOI UNE SESSION? ===

# Sans session: Chaque appel à httpx.get() ouvre une nouvelle connexion
# Lent! Beaucoup de requêtes = beaucoup de connexions

# Avec session: Une seule connexion, réutilisée pour plusieurs requêtes
# Rapide! Une connexion = plusieurs requêtes

# Analogie:
# Sans session = À chaque fois, tu appelles un taxi, il fait une course, puis disparaît
# Avec session = Tu loues un taxi pour la journée, il t'attends

# Une session mémorise aussi:
# - Les cookies (pour les sessions utilisateur)
# - Les headers par défaut
# - La configuration (timeouts, proxies, etc)


# === CRÉER UNE SESSION ===

# Syntaxe basique:
with httpx.Client() as client:
    response = client.get("https://api.example.com/data")
    print(response.json())

# Explications:
# httpx.Client() = Crée une session (appelée "client" dans httpx)
# with = Context manager (ferme auto la session à la fin)
# client.get() = Utilise la session pour GET
# client.post() = Utilise la session pour POST
# etc.

# C'est plus sûr que:
# client = httpx.Client()
# client.get(...)
# client.close()  # Tu dois fermer manuellement!


# === PLUSIEURS REQUÊTES AVEC UNE SESSION ===

# Exemple: Appeler plusieurs endpoints d'une API

with httpx.Client() as client:
    # Requête 1
    response1 = client.get("https://api.example.com/users")
    users = response1.json()
    print(f"Reçu {len(users)} utilisateurs")
    
    # Requête 2
    response2 = client.post(
        "https://api.example.com/users",
        json={"name": "Nouveau", "email": "nouveau@example.com"}
    )
    new_user = response2.json()
    print(f"Utilisateur créé avec id: {new_user['id']}")
    
    # Requête 3
    response3 = client.delete(f"https://api.example.com/users/{new_user['id']}")
    print(f"Status: {response3.status_code}")

# Explications:
# Une seule connexion pour les 3 requêtes!
# Les cookies sont automatiquement gérés


# === SESSION AVEC HEADERS PAR DÉFAUT ===

# Ajouter des headers qu'on utilise partout

with httpx.Client(
    headers={
        "Authorization": "Bearer my_token",
        "User-Agent": "MyApp/1.0"
    }
) as client:
    # Ces headers seront dans TOUTES les requêtes
    response1 = client.get("https://api.example.com/users")
    response2 = client.get("https://api.example.com/posts")
    response3 = client.post("https://api.example.com/data", json={...})

# Explications:
# Tu ne dois pas répéter les headers à chaque requête
# Très utile pour l'authentification (Authorization bearer token)


# === SESSION AVEC CONFIGURATION ===

# Configurer la session

with httpx.Client(
    base_url="https://api.example.com",
    timeout=10.0,  # 10 secondes
    headers={"Authorization": "Bearer token"}
) as client:
    # URL courte (base_url auto-ajouté)
    response = client.get("/users")
    # Équivalent à: https://api.example.com/users
    
    print(response.json())

# Explications:
# base_url = URL de base à préfixer à toutes les requêtes
# timeout = Délai d'attente pour toutes les requêtes
# headers = Headers par défaut pour toutes les requêtes


# === COOKIES DANS UNE SESSION ===

# Les cookies sont automatiquement gérés

with httpx.Client() as client:
    # Première requête: le serveur envoie un cookie
    response1 = client.get("https://example.com/login")
    # httpx stocke automatiquement le cookie
    
    # Deuxième requête: le cookie est envoyé auto
    response2 = client.get("https://example.com/dashboard")
    # Le serveur pense qu'on est "connecté"

# Explications:
# httpx mémorise les cookies entre les requêtes
# Comme un vrai navigateur!
# Très utile pour les sessions utilisateur


# === AFFICHER LES COOKIES ===

with httpx.Client() as client:
    response = client.get("https://example.com")
    
    # Voir tous les cookies
    print(client.cookies)
    # Affiche: {'session_id': 'abc123', ...}
    
    # Accéder à un cookie spécifique
    session_id = client.cookies.get("session_id")
    print(session_id)
    # Affiche: abc123


# === AJOUTER DES COOKIES MANUELLEMENT ===

# Utile si tu connais déjà le cookie

with httpx.Client(
    cookies={"session_id": "abc123def456"}
) as client:
    response = client.get("https://example.com/dashboard")
    # Le serveur recevra le cookie

# Explications:
# cookies = Dictionnaire des cookies à envoyer
# Utile pour continuer une session existante


[OK] AUTHENTIFICATION

# === AUTHENTIFICATION BASIQUE ===

# Envoyer un username et password

response = httpx.get(
    "https://api.example.com/data",
    auth=("username", "password")
)

# httpx encode automatiquement et ajoute le header:
# Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

# Explications:
# auth = Tuple (username, password)
# httpx encode en base64 automatiquement
# Très simple!


# === AUTHENTIFICATION PAR TOKEN (BEARER) ===

# Beaucoup d'APIs utilisent des tokens

response = httpx.get(
    "https://api.example.com/data",
    headers={"Authorization": "Bearer my_secret_token"}
)

# Ou avec une session:
with httpx.Client(
    headers={"Authorization": "Bearer my_secret_token"}
) as client:
    response = client.get("https://api.example.com/data")

# Explications:
# Bearer token = Token d'authentification pour les APIs modernes
# Header Authorization: Bearer <token>
# Plus sécurisé que username/password


# === AUTHENTIFICATION PERSONNALISÉE ===

# Si ton serveur utilise une auth spéciale

# Ajouter juste le header:
response = httpx.get(
    "https://api.example.com/data",
    headers={"X-API-Key": "my_api_key"}
)

# Explications:
# X-API-Key = En-tête personnalisé (différent selon l'API)
# À adapter selon la documentation de l'API


[OK] PARAMÈTRES DE REQUÊTE

# === PARAMÈTRES URL ===

# Passer des paramètres dans l'URL

response = httpx.get(
    "https://api.example.com/search",
    params={
        "q": "python",
        "limit": 10,
        "offset": 0
    }
)

# httpx construit:
# https://api.example.com/search?q=python&limit=10&offset=0

# Explications:
# params = Dictionnaire des paramètres
# httpx encode et ajoute automatiquement
# Plus sûr que de concaténer des strings


# === ENCODING AUTOMATIQUE ===

# Les caractères spéciaux sont encodés automatiquement

response = httpx.get(
    "https://api.example.com/search",
    params={"q": "hello world", "name": "Jean Dupont"}
)

# Résultat:
# https://api.example.com/search?q=hello+world&name=Jean+Dupont
# (espaces encodés)

# Explications:
# Espace = %20 ou +
# Accents = Encodés en UTF-8
# httpx s'en charge automatiquement


# === LISTES DE PARAMÈTRES ===

# Passer plusieurs valeurs pour un paramètre

response = httpx.get(
    "https://api.example.com/filter",
    params=[
        ("category", "python"),
        ("category", "web"),
        ("category", "data")
    ]
)

# Résultat:
# https://api.example.com/filter?category=python&category=web&category=data

# Explications:
# Liste de tuples = paramètres répétés
# Courant dans les APIs (filtres multiples)


[OK] CONTENU DU CORPS (BODY)

# === ENVOYER DU JSON ===

response = httpx.post(
    "https://api.example.com/users",
    json={
        "name": "Jean",
        "email": "jean@example.com",
        "age": 30,
        "tags": ["python", "web"]
    }
)

# httpx:
# 1. Convertit le dictionnaire en JSON
# 2. Ajoute l'en-tête: Content-Type: application/json
# 3. Envoie au serveur

# Afficher ce qui a été envoyé:
print(response.request.content)
# Affiche: b'{"name": "Jean", ...}'

# Explications:
# json= = Convertit en JSON automatiquement
# Très courant pour les APIs modernes


# === ENVOYER DU TEXTE BRUT ===

response = httpx.post(
    "https://api.example.com/log",
    content="Ceci est un log texte"
)

# httpx envoie le texte brut (text/plain)

# Explications:
# content= = Texte brut (string)
# Utilisé pour les logs, les fichiers texte, etc


# === ENVOYER DES DONNÉES BINAIRES ===

# Envoyer un fichier

with open("image.png", "rb") as f:
    response = httpx.post(
        "https://api.example.com/upload",
        content=f.read(),
        headers={"Content-Type": "image/png"}
    )

# Explications:
# content = Données binaires (bytes)
# Content-Type = Important pour dire au serveur le type


# === ENVOYER UN FORMULAIRE ===

response = httpx.post(
    "https://example.com/login",
    data={
        "username": "jean",
        "password": "secret123"
    }
)

# httpx ajoute l'en-tête:
# Content-Type: application/x-www-form-urlencoded

# Résultat envoyé: username=jean&password=secret123

# Explications:
# data= = Formulaire (pas JSON)
# URL-encoded = username=value&name=value


# === ENVOYER UN FICHIER (MULTIPART) ===

# Pour les uploads de fichiers

with open("document.pdf", "rb") as f:
    response = httpx.post(
        "https://api.example.com/documents",
        files={
            "file": ("document.pdf", f, "application/pdf"),
            "description": (None, "Mon document")
        }
    )

# Explications:
# files= = Upload multipart/form-data
# ("filename", file_object, content_type)
# (None, "text") = Données texte sans fichier


# === OBTENIR LE CONTENU ENVOYÉ ===

response = httpx.post(
    "https://api.example.com/data",
    json={"name": "Jean"}
)

# Afficher la requête:
print(response.request)
# Affiche: <Request [POST https://api.example.com/data]>

# Afficher le contenu envoyé:
print(response.request.content)
# Affiche: b'{"name": "Jean"}'

# Afficher les headers envoyés:
print(response.request.headers)
# Affiche: {'content-type': 'application/json', ...}


[OK] GESTION DES ERREURS DÉTAILLÉE

# === EXCEPTIONS COURANTES ===

import httpx

# Exception 1: Timeout (Pas de réponse à temps)
try:
    response = httpx.get(
        "https://api.example.com/data",
        timeout=2  # 2 secondes
    )
except httpx.TimeoutException as e:
    print(f"Timeout! La requête a pris trop de temps")
    print(f"Erreur: {e}")

# Exception 2: Erreur de connexion
try:
    response = httpx.get("https://api.example.com/data")
except httpx.ConnectError as e:
    print(f"Erreur de connexion! Vérifiez votre internet")
    print(f"Erreur: {e}")

# Exception 3: HTTP Status Error (4xx, 5xx)
try:
    response = httpx.get("https://api.example.com/data")
    response.raise_for_status()  # Lève une exception si erreur
except httpx.HTTPStatusError as e:
    print(f"Erreur HTTP {e.response.status_code}")
    print(f"Contenu: {e.response.text}")

# Exception 4: Request Error (Générale)
try:
    response = httpx.get("https://api.example.com/data")
except httpx.RequestError as e:
    print(f"Erreur générale: {e}")

# Exception 5: Erreur de décodage JSON
try:
    response = httpx.get(