# Fichier: python_cheats/cheatsheets/tempfile.txt
# Cheatsheet Module tempfile - Guide Complet sur les Fichiers Temporaires


═══════════════════════════════════════════════════════════════════════════════
[COURS] POUR LES DÉBUTANTS - COMPRENDRE LES FICHIERS TEMPORAIRES
═══════════════════════════════════════════════════════════════════════════════

# [?] QU'EST-CE QU'UN FICHIER TEMPORAIRE ?
"""
Un fichier temporaire est un fichier que votre programme crée pour stocker
des données pendant son exécution, puis supprime automatiquement quand il
n'en a plus besoin.

ANALOGIE : C'est comme un brouillon sur lequel vous écrivez pendant que
vous travaillez, puis que vous jetez une fois le travail terminé.
"""

# [IDEE] POURQUOI UTILISER DES FICHIERS TEMPORAIRES ?

# 1. Trop de données pour la mémoire RAM
"""
Exemple : Vous téléchargez une vidéo de 2 GB. Plutôt que de tout garder
en mémoire (ce qui ralentirait votre ordinateur), vous la sauvegardez
dans un fichier temporaire pendant le traitement.
"""

# 2. Partager des données entre programmes
"""
Exemple : Votre programme Python génère un fichier CSV, puis appelle
Excel pour l'ouvrir. Le fichier temporaire sert d'intermédiaire.
"""

# 3. Sécurité et isolation
"""
Exemple : Vous testez du code qui pourrait planter. Les fichiers
temporaires sont isolés et nettoyés automatiquement, même en cas d'erreur.
"""

# 4. Éviter les conflits de noms
"""
Sans tempfile : Si vous créez "data.txt", un autre programme pourrait
utiliser le même nom -> conflit !

Avec tempfile : Chaque fichier a un nom unique automatique comme
"tmp_abc123_xyz.txt" -> pas de conflit possible.
"""


# [OBJECTIF] EXEMPLE CONCRET POUR DÉBUTANTS

# Imaginez que vous voulez télécharger une image, la redimensionner,
# puis l'enregistrer. Voici SANS et AVEC tempfile :

# [X] SANS tempfile (problématique)
import requests
from PIL import Image

# Télécharger
response = requests.get('https://example.com/image.jpg')
with open('temp_image.jpg', 'wb') as f:  # Nom fixe = risque de conflit
    f.write(response.content)

# Redimensionner
img = Image.open('temp_image.jpg')
img = img.resize((800, 600))
img.save('result.jpg')

# [ATTENTION] PROBLÈMES :
# - Si le programme crash, "temp_image.jpg" reste sur le disque
# - Si vous lancez 2 fois le programme, conflit de nom
# - Vous devez penser à supprimer le fichier manuellement


# [OK] AVEC tempfile (solution propre)
import requests
from PIL import Image
import tempfile
import os

# Télécharger dans un fichier temporaire
with tempfile.NamedTemporaryFile(suffix='.jpg', delete=False) as tmp:
    response = requests.get('https://example.com/image.jpg')
    tmp.write(response.content)
    temp_path = tmp.name  # Nom unique généré automatiquement

try:
    # Redimensionner
    img = Image.open(temp_path)
    img = img.resize((800, 600))
    img.save('result.jpg')
finally:
    # Nettoyer (même si erreur)
    os.unlink(temp_path)

# [OK] AVANTAGES :
# - Nom unique automatique (pas de conflit)
# - Nettoyage garanti même si erreur
# - Code plus propre et sécurisé


# [DOCS] VOCABULAIRE IMPORTANT

"""
• Fichier temporaire : Fichier créé temporairement, puis supprimé
• Context manager : Bloc 'with' qui gère automatiquement la fermeture
• Descripteur de fichier : Identifiant numérique pour un fichier ouvert
• Buffer : Zone mémoire temporaire avant écriture sur disque
• Flush : Forcer l'écriture du buffer sur le disque
• Mode binaire : Fichier contenant des bytes (images, vidéos, etc.)
• Mode texte : Fichier contenant du texte lisible
• Permissions : Qui peut lire/écrire le fichier (sécurité)
"""


# [JAPANESE_SYMBOL_FOR_BEGINNER] RÈGLES D'OR POUR DÉBUTANTS

# Règle 1 : TOUJOURS utiliser 'with' (context manager)
# [OK] BON
with tempfile.TemporaryFile() as tmp:
    tmp.write(b'data')
# Fichier automatiquement fermé et supprimé

# [X] MAUVAIS
tmp = tempfile.TemporaryFile()
tmp.write(b'data')
# Vous devez penser à fermer : tmp.close()


# Règle 2 : Spécifier le mode (binaire 'b' ou texte 't')
# [OK] BON - Mode binaire pour images, vidéos, etc.
with tempfile.TemporaryFile(mode='wb') as tmp:
    tmp.write(b'binary data')  # Noter le 'b' devant la chaîne

# [OK] BON - Mode texte pour du texte
with tempfile.TemporaryFile(mode='wt') as tmp:
    tmp.write('text data')  # Pas de 'b'


# Règle 3 : Utiliser suffix pour l'extension
with tempfile.NamedTemporaryFile(suffix='.jpg') as tmp:
    print(tmp.name)  # quelque_chose.jpg
    # Utile si un programme externe a besoin de l'extension


# Règle 4 : Faire flush() avant de partager le fichier
with tempfile.NamedTemporaryFile(delete=False) as tmp:
    tmp.write(b'data')
    tmp.flush()  # [ATTENTION] IMPORTANT : force l'écriture
    
    # Maintenant un autre programme peut lire le fichier
    other_program(tmp.name)


═══════════════════════════════════════════════════════════════════════════════
[GUIDE] GUIDE PROGRESSIF - DU PLUS SIMPLE AU PLUS AVANCÉ
═══════════════════════════════════════════════════════════════════════════════


[OK] INTRODUCTION

# Le module tempfile permet de créer des fichiers et répertoires temporaires
# de manière sécurisée et portable sur tous les systèmes d'exploitation.

import tempfile

# Avantages:
# [OK] Noms uniques automatiques (évite les collisions)
# [OK] Suppression automatique après utilisation
# [OK] Sécurité (permissions restrictives)
# [OK] Portable (fonctionne sur Windows, Linux, Mac)
# [OK] Gestion automatique des ressources


═══════════════════════════════════════════════════════════════════════════════
[JAPANESE_SYMBOL_FOR_BEGINNER] NIVEAU 1 : TEMPFILE.TEMPORARYFILE - LE PLUS SIMPLE
═══════════════════════════════════════════════════════════════════════════════

"""
[OBJECTIF] QUAND UTILISER TemporaryFile ?
• Vous avez besoin d'un fichier temporaire simple
• Vous n'avez PAS besoin que d'autres programmes y accèdent
• Vous voulez la solution la plus sécurisée (pas de nom visible)

[IDEE] CARACTÉRISTIQUE PRINCIPALE :
Le fichier n'a PAS de nom visible dans le système de fichiers.
C'est comme un fichier "fantôme" que seul votre programme voit.
"""

import tempfile

# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 1 : Votre premier fichier temporaire (MODE BINAIRE)
# ═══════════════════════════════════════════════════════════════════════════

"""
Explications ligne par ligne :
"""

# 1. Créer le fichier temporaire avec 'with'
with tempfile.TemporaryFile() as tmp:
    # tmp est maintenant un fichier ouvert
    # Le 'with' garantit qu'il sera fermé et supprimé automatiquement
    
    # 2. Écrire dans le fichier (mode binaire par défaut)
    tmp.write(b'Hello World')  # Le 'b' signifie "bytes" (binaire)
    
    # 3. IMPORTANT : Le curseur est maintenant à la fin du fichier
    # Pour lire, il faut revenir au début
    tmp.seek(0)  # seek(0) = retour au début du fichier
    
    # 4. Lire le contenu
    content = tmp.read()  # Lit tout le contenu
    print(content)  # Affiche: b'Hello World'
    print(type(content))  # Affiche: <class 'bytes'>

# 5. À la sortie du 'with', le fichier est automatiquement :
#    - Fermé
#    - Supprimé du disque
# Vous n'avez rien à faire !


# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 2 : Mode TEXTE (plus simple pour les débutants)
# ═══════════════════════════════════════════════════════════════════════════

"""
Mode texte : Pour écrire du texte normal (pas besoin de 'b')
"""

with tempfile.TemporaryFile(mode='w+t') as tmp:
    # mode='w+t' signifie :
    # 'w' = write (écriture)
    # '+' = aussi la lecture
    # 't' = text (texte, pas binaire)
    
    # Écrire du texte normal (sans 'b')
    tmp.write('Bonjour le monde!\n')
    tmp.write('Ligne 2\n')
    tmp.write('Ligne 3\n')
    
    # Revenir au début
    tmp.seek(0)
    
    # Lire tout
    texte = tmp.read()
    print(texte)
    # Affiche:
    # Bonjour le monde!
    # Ligne 2
    # Ligne 3


# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 3 : Comprendre seek() - Le curseur dans le fichier
# ═══════════════════════════════════════════════════════════════════════════

"""
Le fichier est comme un ruban de cassette : il y a un "curseur" qui
indique où vous êtes dans le fichier.

seek(0) = retour au début
seek(10) = aller à la position 10
"""

with tempfile.TemporaryFile(mode='w+t') as tmp:
    # Écrire
    tmp.write('ABCDEFGHIJ')  # 10 caractères
    
    # Le curseur est maintenant à la position 10 (après J)
    print(f"Position actuelle: {tmp.tell()}")  # Affiche: 10
    
    # Lire maintenant ne donnerait rien (on est à la fin)
    print(f"Lecture à la fin: '{tmp.read()}'")  # Affiche: ''
    
    # Retourner au début
    tmp.seek(0)
    print(f"Position après seek(0): {tmp.tell()}")  # Affiche: 0
    
    # Maintenant on peut lire
    print(f"Lecture: '{tmp.read()}'")  # Affiche: 'ABCDEFGHIJ'
    
    # Aller à une position spécifique
    tmp.seek(5)  # Aller au milieu
    print(f"Lecture depuis position 5: '{tmp.read()}'")  # Affiche: 'FGHIJ'


# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 4 : Cas d'usage réel - Traiter un gros fichier
# ═══════════════════════════════════════════════════════════════════════════

"""
Scenario : Vous téléchargez un gros fichier, vous voulez le traiter,
mais vous ne voulez pas tout garder en mémoire.
"""

import requests

def download_and_process(url):
    """Télécharge un fichier et le traite."""
    
    with tempfile.TemporaryFile() as tmp:
        # Télécharger dans le fichier temporaire
        print("Téléchargement en cours...")
        response = requests.get(url, stream=True)
        
        # Écrire par morceaux (chunks)
        for chunk in response.iter_content(chunk_size=8192):
            tmp.write(chunk)
        
        print("Téléchargement terminé!")
        
        # Revenir au début pour traiter
        tmp.seek(0)
        
        # Traiter le fichier ligne par ligne
        # (imagine que c'est un fichier texte)
        tmp_text = tmp.read().decode('utf-8')
        lines = tmp_text.split('\n')
        
        print(f"Nombre de lignes: {len(lines)}")
        
        return lines
    # Fichier automatiquement supprimé ici


# ═══════════════════════════════════════════════════════════════════════════
# [GUIDE] COMPRENDRE LES MODES DE FICHIERS
# ═══════════════════════════════════════════════════════════════════════════

"""
Les modes les plus courants :

MODE BINAIRE (pour images, vidéos, fichiers non-texte) :
• 'w+b' ou 'wb' - Écriture binaire (défaut de TemporaryFile)
• 'r+b' ou 'rb' - Lecture binaire

MODE TEXTE (pour du texte normal) :
• 'w+t' ou 'wt' - Écriture texte
• 'r+t' ou 'rt' - Lecture texte

Le '+' signifie lecture ET écriture.
Sans '+', vous ne pouvez qu'écrire OU lire, pas les deux.
"""

# Exemples pratiques :

# Mode binaire - Pour une image
with tempfile.TemporaryFile(mode='w+b') as tmp:
    # Écrire des bytes
    tmp.write(b'\x89PNG\r\n\x1a\n')  # Début d'un fichier PNG
    tmp.seek(0)
    data = tmp.read()
    print(type(data))  # <class 'bytes'>

# Mode texte - Pour du texte
with tempfile.TemporaryFile(mode='w+t') as tmp:
    # Écrire du texte normal
    tmp.write('Bonjour')
    tmp.seek(0)
    data = tmp.read()
    print(type(data))  # <class 'str'>

═══════════════════════════════════════════════════════════════════════════════
[JAPANESE_SYMBOL_FOR_BEGINNER] NIVEAU 2 : TEMPFILE.NAMEDTEMPORARYFILE - FICHIER AVEC NOM
═══════════════════════════════════════════════════════════════════════════════

"""
[OBJECTIF] QUAND UTILISER NamedTemporaryFile ?
• Vous avez besoin que d'AUTRES PROGRAMMES accèdent au fichier
• Vous devez passer le chemin du fichier à une fonction/commande
• Vous voulez voir où le fichier est créé (pour déboguer)

[IDEE] DIFFÉRENCE AVEC TemporaryFile :
NamedTemporaryFile a un NOM VISIBLE dans le système.
Exemple : /tmp/tmpxyz123.txt

TemporaryFile n'a PAS de nom visible (plus sécurisé mais moins flexible)
"""

import tempfile
import os

# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 1 : Votre premier fichier nommé
# ═══════════════════════════════════════════════════════════════════════════

with tempfile.NamedTemporaryFile() as tmp:
    # IMPORTANT : tmp.name contient le chemin du fichier
    print(f"Fichier créé ici : {tmp.name}")
    # Exemple sous Linux: /tmp/tmpxyz123
    # Exemple sous Windows: C:\Users\...\AppData\Local\Temp\tmpxyz123
    
    # Écrire dans le fichier
    tmp.write(b'Mes données importantes')
    
    # [ATTENTION] ASTUCE IMPORTANTE : faire flush() !
    tmp.flush()  # Force l'écriture sur le disque
    
    # Maintenant, un autre programme peut lire ce fichier
    # Vérifier qu'il existe
    print(f"Le fichier existe ? {os.path.exists(tmp.name)}")  # True
    
    # Un autre programme peut maintenant lire tmp.name
    with open(tmp.name, 'rb') as f:
        contenu = f.read()
        print(f"Contenu lu : {contenu}")

# À la sortie du 'with', le fichier est SUPPRIMÉ automatiquement
print(f"Le fichier existe encore ? {os.path.exists(tmp.name)}")  # False


# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 2 : Garder le fichier après fermeture (delete=False)
# ═══════════════════════════════════════════════════════════════════════════

"""
Par défaut, NamedTemporaryFile supprime le fichier à la fermeture.
Avec delete=False, le fichier reste sur le disque.

[ATTENTION] IMPORTANT : Vous devez alors le supprimer MANUELLEMENT !
"""

# Créer un fichier qui persiste
with tempfile.NamedTemporaryFile(delete=False) as tmp:
    tmp.write(b'Données persistantes')
    chemin_fichier = tmp.name  # Sauvegarder le chemin
    print(f"Fichier créé : {chemin_fichier}")

# Le fichier existe TOUJOURS ici
print(f"Existe encore ? {os.path.exists(chemin_fichier)}")  # True

# Vous pouvez le réutiliser
with open(chemin_fichier, 'rb') as f:
    print(f"Contenu : {f.read()}")

# [ATTENTION] N'OUBLIEZ PAS de le supprimer manuellement !
os.unlink(chemin_fichier)  # Supprimer le fichier
print(f"Supprimé ? {not os.path.exists(chemin_fichier)}")  # True


# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 3 : Personnaliser le nom avec suffix et prefix
# ═══════════════════════════════════════════════════════════════════════════

"""
Vous pouvez contrôler le nom du fichier avec :
• suffix : Extension à la fin (exemple: .txt, .jpg)
• prefix : Préfixe au début (exemple: mon_app_)
"""

with tempfile.NamedTemporaryFile(
    suffix='.txt',      # Fichier se terminera par .txt
    prefix='mon_app_',  # Fichier commencera par mon_app_
    mode='w+t'          # Mode texte
) as tmp:
    print(f"Nom du fichier : {tmp.name}")
    # Exemple : /tmp/mon_app_xyz123.txt
    
    tmp.write('Mon contenu texte')
    
    # Le suffix est utile si un programme externe
    # a besoin de l'extension pour comprendre le type de fichier


# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 4 : Cas réel - Passer un fichier à un programme externe
# ═══════════════════════════════════════════════════════════════════════════

"""
Scénario : Vous générez des données en Python, puis appelez un
programme externe (comme ImageMagick, ffmpeg, etc.) pour les traiter.
"""

import subprocess
import json

# Créer un fichier JSON temporaire
with tempfile.NamedTemporaryFile(
    mode='w',
    suffix='.json',
    delete=False
) as tmp:
    # Données à traiter
    donnees = {
        'nom': 'Alice',
        'age': 30,
        'ville': 'Paris'
    }
    
    # Écrire en JSON
    json.dump(donnees, tmp, indent=2)
    tmp.flush()  # [ATTENTION] IMPORTANT : forcer l'écriture
    
    chemin_json = tmp.name
    print(f"JSON créé : {chemin_json}")

# Appeler un programme externe qui lit ce JSON
# (exemple fictif)
try:
    # subprocess.run(['mon_programme', '--input', chemin_json])
    print(f"Programme externe pourrait lire : {chemin_json}")
finally:
    # [ATTENTION] TOUJOURS nettoyer dans un bloc finally
    # (pour supprimer même si erreur)
    os.unlink(chemin_json)


# ═══════════════════════════════════════════════════════════════════════════
# [IDEE] COMPRENDRE flush() - POURQUOI C'EST IMPORTANT
# ═══════════════════════════════════════════════════════════════════════════

"""
Quand vous écrivez dans un fichier, les données vont d'abord dans un
"buffer" (mémoire temporaire), puis sont écrites sur le disque.

Le problème : Si un autre programme essaie de lire le fichier AVANT
que le buffer soit vidé, il verra un fichier vide ou incomplet !

Solution : tmp.flush() force l'écriture immédiate sur le disque.
"""

# Exemple du problème :
with tempfile.NamedTemporaryFile(delete=False) as tmp:
    tmp.write(b'Données importantes')
    chemin = tmp.name
    
    # [X] SANS flush() - Risqué !
    # Un autre programme pourrait voir un fichier vide ici
    
    # [OK] AVEC flush() - Sûr !
    tmp.flush()
    # Maintenant le fichier est complet sur le disque

os.unlink(chemin)


# ═══════════════════════════════════════════════════════════════════════════
# [GUIDE] COMPRENDRE delete=True vs delete=False
# ═══════════════════════════════════════════════════════════════════════════

"""
┌────────────────────┬──────────────────────────────────────────┐
│  delete=True       │  Fichier SUPPRIMÉ à la sortie du 'with'  │
│  (défaut)          │  [OK] Pas besoin de nettoyer manuellement   │
│                    │  [X] Le fichier disparaît immédiatement    │
├────────────────────┼──────────────────────────────────────────┤
│  delete=False      │  Fichier RESTE sur le disque             │
│                    │  [OK] Vous pouvez le réutiliser après       │
│                    │  [X] Vous DEVEZ le supprimer manuellement  │
└────────────────────┴──────────────────────────────────────────┘
"""

# delete=True (comportement par défaut)
with tempfile.NamedTemporaryFile() as tmp:
    chemin = tmp.name
    tmp.write(b'data')
# Fichier SUPPRIMÉ ici automatiquement

# delete=False (fichier persiste)
with tempfile.NamedTemporaryFile(delete=False) as tmp:
    chemin = tmp.name
    tmp.write(b'data')
# Fichier EXISTE TOUJOURS ici
os.unlink(chemin)  # Vous devez le supprimer

═══════════════════════════════════════════════════════════════════════════════
[JAPANESE_SYMBOL_FOR_BEGINNER] NIVEAU 3 : TEMPFILE.TEMPORARYDIRECTORY - RÉPERTOIRE TEMPORAIRE
═══════════════════════════════════════════════════════════════════════════════

"""
[OBJECTIF] QUAND UTILISER TemporaryDirectory ?
• Vous avez besoin de créer PLUSIEURS fichiers temporaires
• Vous voulez une structure de dossiers temporaire
• Vous voulez tout nettoyer d'un coup automatiquement

[IDEE] AVANTAGE PRINCIPAL :
Crée un dossier complet, supprime TOUT son contenu automatiquement
(fichiers, sous-dossiers, tout !) à la sortie du 'with'.

[COURS] ANALOGIE :
C'est comme créer un dossier "Brouillons" sur votre bureau,
y travailler, puis tout jeter à la poubelle d'un coup.
"""

import tempfile
import os

# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 1 : Créer votre premier répertoire temporaire
# ═══════════════════════════════════════════════════════════════════════════

with tempfile.TemporaryDirectory() as tmpdir:
    # tmpdir est le CHEMIN du répertoire temporaire
    print(f"Répertoire créé : {tmpdir}")
    # Exemple : /tmp/tmpxyz123/
    
    # Vérifier qu'il existe
    print(f"Existe ? {os.path.exists(tmpdir)}")  # True
    print(f"C'est un dossier ? {os.path.isdir(tmpdir)}")  # True
    
    # Créer des fichiers dedans
    fichier1 = os.path.join(tmpdir, 'fichier1.txt')
    fichier2 = os.path.join(tmpdir, 'fichier2.txt')
    
    # os.path.join() combine correctement les chemins
    # (fonctionne sur Windows et Linux)
    
    with open(fichier1, 'w') as f:
        f.write('Contenu du fichier 1')
    
    with open(fichier2, 'w') as f:
        f.write('Contenu du fichier 2')
    
    # Lister le contenu du répertoire
    contenu = os.listdir(tmpdir)
    print(f"Fichiers créés : {contenu}")
    # Affiche : ['fichier1.txt', 'fichier2.txt']

# À la sortie du 'with' :
# - fichier1.txt est SUPPRIMÉ
# - fichier2.txt est SUPPRIMÉ
# - Le répertoire tmpdir est SUPPRIMÉ
# Tout est nettoyé automatiquement !

print(f"Le répertoire existe encore ? {os.path.exists(tmpdir)}")  # False


# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 2 : Créer une structure de dossiers complexe
# ═══════════════════════════════════════════════════════════════════════════

"""
Vous pouvez créer une structure complète :
tmpdir/
  ├── input/
  │   ├── data1.txt
  │   └── data2.txt
  ├── output/
  └── logs/
"""

with tempfile.TemporaryDirectory() as tmpdir:
    print(f"Workspace temporaire : {tmpdir}")
    
    # Créer sous-répertoires
    input_dir = os.path.join(tmpdir, 'input')
    output_dir = os.path.join(tmpdir, 'output')
    logs_dir = os.path.join(tmpdir, 'logs')
    
    os.mkdir(input_dir)   # Créer dossier input
    os.mkdir(output_dir)  # Créer dossier output
    os.mkdir(logs_dir)    # Créer dossier logs
    
    # Créer des fichiers dans input/
    data1 = os.path.join(input_dir, 'data1.txt')
    data2 = os.path.join(input_dir, 'data2.txt')
    
    with open(data1, 'w') as f:
        f.write('Données 1')
    
    with open(data2, 'w') as f:
        f.write('Données 2')
    
    # Vérifier la structure
    print("\n[DOSSIER] Structure créée :")
    for root, dirs, files in os.walk(tmpdir):
        # os.walk() parcourt récursivement tous les sous-dossiers
        level = root.replace(tmpdir, '').count(os.sep)
        indent = ' ' * 2 * level
        print(f'{indent}{os.path.basename(root)}/')
        subindent = ' ' * 2 * (level + 1)
        for file in files:
            print(f'{subindent}{file}')

# Tout est supprimé automatiquement (récursif) !


# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 3 : Cas réel - Extraire et traiter une archive
# ═══════════════════════════════════════════════════════════════════════════

"""
Scénario : Vous téléchargez une archive ZIP, vous voulez extraire
les fichiers, les traiter, puis tout nettoyer.
"""

import zipfile
import shutil

def traiter_archive(chemin_zip):
    """Extrait une archive, traite les fichiers, nettoie."""
    
    with tempfile.TemporaryDirectory() as tmpdir:
        print(f"Extraction dans : {tmpdir}")
        
        # Extraire l'archive
        with zipfile.ZipFile(chemin_zip, 'r') as zip_ref:
            zip_ref.extractall(tmpdir)
        
        print(f"Fichiers extraits : {os.listdir(tmpdir)}")
        
        # Traiter chaque fichier
        resultats = []
        for nom_fichier in os.listdir(tmpdir):
            chemin_complet = os.path.join(tmpdir, nom_fichier)
            
            if os.path.isfile(chemin_complet):
                with open(chemin_complet, 'r') as f:
                    contenu = f.read()
                    # Traiter le contenu
                    resultat = contenu.upper()  # Exemple simple
                    resultats.append(resultat)
        
        return resultats
    
    # Ici, tous les fichiers extraits sont automatiquement supprimés !
    # Vous n'avez pas besoin de nettoyer manuellement.


# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 4 : Workspace pour un build/compilation
# ═══════════════════════════════════════════════════════════════════════════

"""
Scénario : Vous compilez un projet, vous avez besoin de dossiers
temporaires pour les fichiers intermédiaires.
"""

def build_project(source_dir):
    """Compile un projet avec un workspace temporaire."""
    
    with tempfile.TemporaryDirectory() as workspace:
        print(f"[OUTIL] Workspace de build : {workspace}")
        
        # Structure pour le build
        build_dir = os.path.join(workspace, 'build')
        dist_dir = os.path.join(workspace, 'dist')
        cache_dir = os.path.join(workspace, 'cache')
        
        os.makedirs(build_dir)
        os.makedirs(dist_dir)
        os.makedirs(cache_dir)
        
        # 1. Copier les sources dans build/
        print("[LISTE] Copie des sources...")
        shutil.copytree(source_dir, os.path.join(build_dir, 'src'))
        
        # 2. "Compiler" (exemple simplifié)
        print("[CONFIG]  Compilation...")
        output_file = os.path.join(dist_dir, 'output.bin')
        with open(output_file, 'w') as f:
            f.write('COMPILED CODE')
        
        # 3. Copier le résultat final vers destination permanente
        print("[PACKAGE] Copie du résultat...")
        shutil.copy(output_file, './resultat_final.bin')
        
        print("[OK] Build terminé!")
    
    # Tous les fichiers temporaires (build, cache) sont supprimés
    # Seul resultat_final.bin reste


# ═══════════════════════════════════════════════════════════════════════════
# [IDEE] COMPRENDRE os.path.join() - Combiner des chemins
# ═══════════════════════════════════════════════════════════════════════════

"""
os.path.join() est ESSENTIEL pour créer des chemins portables.

[X] MAUVAIS (ne marche pas sur tous les OS) :
chemin = tmpdir + '/fichier.txt'  # '/' ne marche pas sur Windows !

[OK] BON (marche partout) :
chemin = os.path.join(tmpdir, 'fichier.txt')

Pourquoi ? Parce que :
• Linux/Mac utilise '/' (slash)
• Windows utilise '\' (backslash)
• os.path.join() utilise automatiquement le bon séparateur
"""

# Exemple pratique
with tempfile.TemporaryDirectory() as tmpdir:
    # [X] Ne faites PAS ça :
    # chemin_mauvais = tmpdir + '/data/file.txt'
    
    # [OK] Faites ça :
    chemin_bon = os.path.join(tmpdir, 'data', 'file.txt')
    
    # Créer le dossier parent
    os.makedirs(os.path.dirname(chemin_bon), exist_ok=True)
    # exist_ok=True : ne pas planter si le dossier existe déjà
    
    # Créer le fichier
    with open(chemin_bon, 'w') as f:
        f.write('Contenu')
    
    print(f"Fichier créé : {chemin_bon}")


# ═══════════════════════════════════════════════════════════════════════════
# [GUIDE] PATTERN COURANT : Organisation d'un workspace
# ═══════════════════════════════════════════════════════════════════════════

"""
Pattern très utilisé en production :
"""

def traitement_avec_workspace(donnees_entree):
    """Pattern professionnel pour organiser un traitement."""
    
    with tempfile.TemporaryDirectory() as workspace:
        # 1. Créer structure
        dossiers = {
            'input': os.path.join(workspace, 'input'),
            'output': os.path.join(workspace, 'output'),
            'temp': os.path.join(workspace, 'temp'),
            'logs': os.path.join(workspace, 'logs'),
        }
        
        for dossier in dossiers.values():
            os.makedirs(dossier)
        
        # 2. Sauvegarder input
        input_file = os.path.join(dossiers['input'], 'data.json')
        with open(input_file, 'w') as f:
            json.dump(donnees_entree, f)
        
        # 3. Traiter
        output_file = os.path.join(dossiers['output'], 'result.json')
        # ... votre code de traitement ...
        
        # 4. Log
        log_file = os.path.join(dossiers['logs'], 'process.log')
        with open(log_file, 'w') as f:
            f.write('Traitement terminé\n')
        
        # 5. Lire résultat
        with open(output_file, 'r') as f:
            resultat = json.load(f)
        
        return resultat
    
    # Workspace nettoyé automatiquement

═══════════════════════════════════════════════════════════════════════════════
[JAPANESE_SYMBOL_FOR_BEGINNER] NIVEAU 4 : COMPRENDRE LES MODES DE FICHIERS (POUR DÉBUTANTS)
═══════════════════════════════════════════════════════════════════════════════

"""
Les "modes" de fichiers sont comme les paramètres de votre traitement de texte :
voulez-vous ouvrir en "lecture seule", "écriture", "les deux" ?

[GUIDE] LES BASES :

'r' = Read (Lire)       - Ouvrir un fichier existant pour le lire
'w' = Write (Écrire)    - Créer/écraser un fichier pour écrire
'a' = Append (Ajouter)  - Ajouter à la fin d'un fichier existant
'+' = Plus              - Lecture ET écriture en même temps

'b' = Binary (Binaire)  - Pour images, vidéos, fichiers non-texte
't' = Text (Texte)      - Pour du texte normal (défaut si non spécifié)
"""

# ═══════════════════════════════════════════════════════════════════════════
# GUIDE DES MODES POUR TEMPFILE
# ═══════════════════════════════════════════════════════════════════════════

import tempfile

# ┌─────────────────────────────────────────────────────────────────────┐
# │ MODE 'w+b' - BINAIRE, LECTURE + ÉCRITURE (défaut TemporaryFile)    │
# └─────────────────────────────────────────────────────────────────────┘
"""
• Utiliser pour : images, vidéos, PDF, fichiers Excel, etc.
• Vous devez écrire des BYTES (avec 'b' devant)
• Exemple : b'Hello' au lieu de 'Hello'
"""

with tempfile.TemporaryFile(mode='w+b') as tmp:
    # Écrire des bytes
    tmp.write(b'Donnees binaires')  # Noter le 'b'
    tmp.seek(0)
    
    # Lire retourne des bytes
    contenu = tmp.read()
    print(type(contenu))  # <class 'bytes'>
    print(contenu)        # b'Donnees binaires'


# ┌─────────────────────────────────────────────────────────────────────┐
# │ MODE 'w+t' - TEXTE, LECTURE + ÉCRITURE                              │
# └─────────────────────────────────────────────────────────────────────┘
"""
• Utiliser pour : fichiers texte, JSON, CSV, etc.
• Vous écrivez du texte NORMAL (sans 'b')
• Plus simple pour les débutants !
"""

with tempfile.TemporaryFile(mode='w+t', encoding='utf-8') as tmp:
    # Écrire du texte normal
    tmp.write('Bonjour le monde')  # Pas de 'b'
    tmp.seek(0)
    
    # Lire retourne une string
    contenu = tmp.read()
    print(type(contenu))  # <class 'str'>
    print(contenu)        # Bonjour le monde


# ┌─────────────────────────────────────────────────────────────────────┐
# │ MODE 'rb' - BINAIRE, LECTURE SEULE                                  │
# └─────────────────────────────────────────────────────────────────────┘
"""
• Utiliser pour : lire un fichier binaire existant
• Vous NE POUVEZ PAS écrire
"""

# Exemple : lire une image
with open('image.jpg', 'rb') as f:
    contenu = f.read()  # bytes de l'image


# ┌─────────────────────────────────────────────────────────────────────┐
# │ MODE 'rt' - TEXTE, LECTURE SEULE                                    │
# └─────────────────────────────────────────────────────────────────────┘
"""
• Utiliser pour : lire un fichier texte existant
• Vous NE POUVEZ PAS écrire
"""

# Exemple : lire un fichier texte
with open('document.txt', 'rt', encoding='utf-8') as f:
    contenu = f.read()  # string


# ═══════════════════════════════════════════════════════════════════════════
# [COURS] EXERCICE PRATIQUE : Quand utiliser quel mode ?
# ═══════════════════════════════════════════════════════════════════════════

"""
Situation -> Mode recommandé

1. Stocker une image téléchargée temporairement
   -> 'w+b' (binaire, lecture + écriture)

2. Créer un fichier JSON temporaire
   -> 'w+t' (texte, lecture + écriture)

3. Traiter un fichier CSV temporairement
   -> 'w+t' (texte, lecture + écriture)

4. Sauvegarder un PDF temporaire
   -> 'w+b' (binaire, lecture + écriture)

5. Logger des messages temporairement
   -> 'w+t' (texte, lecture + écriture)
"""

# Exemple 1 : Image (BINAIRE)
import requests

with tempfile.TemporaryFile(mode='w+b') as tmp:
    # Télécharger image
    response = requests.get('https://example.com/image.jpg')
    tmp.write(response.content)  # content = bytes
    tmp.seek(0)
    
    # Traiter l'image
    image_data = tmp.read()


# Exemple 2 : JSON (TEXTE)
import json

with tempfile.TemporaryFile(mode='w+t', encoding='utf-8') as tmp:
    # Créer données
    data = {'nom': 'Alice', 'age': 30}
    
    # Écrire JSON
    json.dump(data, tmp)
    tmp.seek(0)
    
    # Relire JSON
    data_lue = json.load(tmp)


# ═══════════════════════════════════════════════════════════════════════════
# [ATTENTION] ERREUR COURANTE : Mélanger binaire et texte
# ═══════════════════════════════════════════════════════════════════════════

# [X] ERREUR 1 : Mode binaire avec string
try:
    with tempfile.TemporaryFile(mode='wb') as tmp:
        tmp.write('texte')  # [X] ERREUR : bytes requis, pas string
except TypeError as e:
    print(f"Erreur : {e}")

# [OK] CORRECTION 1 : Ajouter 'b'
with tempfile.TemporaryFile(mode='wb') as tmp:
    tmp.write(b'texte')  # [OK] OK : bytes


# [X] ERREUR 2 : Mode texte avec bytes
try:
    with tempfile.TemporaryFile(mode='wt') as tmp:
        tmp.write(b'bytes')  # [X] ERREUR : string requis, pas bytes
except TypeError as e:
    print(f"Erreur : {e}")

# [OK] CORRECTION 2 : Retirer 'b'
with tempfile.TemporaryFile(mode='wt') as tmp:
    tmp.write('texte')  # [OK] OK : string


# ═══════════════════════════════════════════════════════════════════════════
# [IDEE] ASTUCE : Comment convertir entre bytes et string
# ═══════════════════════════════════════════════════════════════════════════

# String -> Bytes
texte = "Bonjour"
bytes_data = texte.encode('utf-8')
print(bytes_data)  # b'Bonjour'

# Bytes -> String
bytes_data = b'Bonjour'
texte = bytes_data.decode('utf-8')
print(texte)  # Bonjour


# ═══════════════════════════════════════════════════════════════════════════
# [GRAPHIQUE] TABLEAU RÉCAPITULATIF DES MODES
# ═══════════════════════════════════════════════════════════════════════════

"""
┌──────────┬──────────┬──────────┬────────────────────────────────┐
│  Mode    │  Type    │  Actions │  Usage typique                 │
├──────────┼──────────┼──────────┼────────────────────────────────┤
│  'w+b'   │ Binaire  │ Lire +   │ Images, vidéos, PDF, Excel     │
│          │          │ Écrire   │ (défaut TemporaryFile)         │
├──────────┼──────────┼──────────┼────────────────────────────────┤
│  'w+t'   │ Texte    │ Lire +   │ Texte, JSON, CSV, logs         │
│          │          │ Écrire   │ (plus simple pour débutants)   │
├──────────┼──────────┼──────────┼────────────────────────────────┤
│  'wb'    │ Binaire  │ Écrire   │ Créer fichier binaire          │
│          │          │ seul.    │                                │
├──────────┼──────────┼──────────┼────────────────────────────────┤
│  'wt'    │ Texte    │ Écrire   │ Créer fichier texte            │
│          │          │ seul.    │                                │
├──────────┼──────────┼──────────┼────────────────────────────────┤
│  'rb'    │ Binaire  │ Lire     │ Lire fichier binaire existant  │
│          │          │ seul.    │                                │
├──────────┼──────────┼──────────┼────────────────────────────────┤
│  'rt'    │ Texte    │ Lire     │ Lire fichier texte existant    │
│          │          │ seul.    │                                │
└──────────┴──────────┴──────────┴────────────────────────────────┘

[IDEE] CONSEIL POUR DÉBUTANTS :
Utilisez 'w+t' pour la plupart de vos besoins (texte, JSON, CSV).
N'utilisez 'w+b' que pour images/vidéos/fichiers binaires.
"""


═══════════════════════════════════════════════════════════════════════════════
[JAPANESE_SYMBOL_FOR_BEGINNER] NIVEAU 5 : SPOOLEDTEMPORARYFILE - OPTIMISATION MÉMOIRE/DISQUE
═══════════════════════════════════════════════════════════════════════════════

"""
[OBJECTIF] QUAND UTILISER SpooledTemporaryFile ?
• Vous ne savez pas si votre fichier sera petit ou gros
• Vous voulez optimiser les performances
• Vous travaillez avec beaucoup de petits fichiers

[IDEE] PRINCIPE :
Le fichier reste EN MÉMOIRE (rapide) tant qu'il est petit.
S'il devient trop gros, il bascule SUR LE DISQUE automatiquement.

[COURS] ANALOGIE :
C'est comme garder vos courses dans les mains si vous avez peu d'articles,
mais prendre un caddie si vous en avez beaucoup.
"""

import tempfile

# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 1 : Fichier reste en mémoire (petit)
# ═══════════════════════════════════════════════════════════════════════════

# Limite : 1 MB (1 048 576 bytes)
with tempfile.SpooledTemporaryFile(max_size=1024*1024) as tmp:
    # Écrire petit contenu (500 bytes)
    tmp.write(b'x' * 500)
    
    # Vérifier si en mémoire ou sur disque
    # _rolled = False -> en mémoire
    # _rolled = True -> sur disque
    print(f"En mémoire ? {not tmp._rolled}")  # True
    print(f"Sur disque ? {tmp._rolled}")      # False
    
    # Le fichier est en mémoire = TRÈS RAPIDE !


# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 2 : Fichier bascule sur disque (gros)
# ═══════════════════════════════════════════════════════════════════════════

# Limite : 1 KB (1024 bytes)
with tempfile.SpooledTemporaryFile(max_size=1024) as tmp:
    # Écrire contenu < limite
    tmp.write(b'x' * 500)
    print(f"Après 500 bytes - En mémoire ? {not tmp._rolled}")  # True
    
    # Écrire plus pour dépasser la limite
    tmp.write(b'y' * 600)  # Total = 1100 bytes > 1024
    print(f"Après 1100 bytes - Sur disque ? {tmp._rolled}")     # True
    
    # Le fichier a automatiquement basculé sur le disque !
    # Vous n'avez rien à faire, ça marche toujours pareil.


# ═══════════════════════════════════════════════════════════════════════════
# EXEMPLE 3 : Cas d'usage réel - Traiter des uploads utilisateur
# ═══════════════════════════════════════════════════════════════════════════

"""
Scénario : Les utilisateurs uploadent des fichiers.
- Petits fichiers (< 5 MB) : rester en mémoire (rapide)
- Gros fichiers (> 5 MB) : basculer sur disque (évite saturation mémoire)
"""

def traiter_upload(fichier_upload):
    """Traite un fichier uploadé de manière optimale."""
    
    # Limite : 5 MB
    with tempfile.SpooledTemporaryFile(max_size=5*1024*1024) as tmp:
        # Lire l'upload
        contenu = fichier_upload.read()
        tmp.write(contenu)
        
        # Déterminer où le fichier est stocké
        if not tmp._rolled:
            print("[OK] Petit fichier - Traité en mémoire (rapide)")
        else:
            print("-> Gros fichier - Basculé sur disque")
        
        # Traiter le fichier (même code dans les 2 cas !)
        tmp.seek(0)
        resultat = traiter_donnees(tmp.read())
        
        return resultat


# ═══════════════════════════════════════════════════════════════════════════
# [IDEE] COMPRENDRE LA DIFFÉRENCE DE PERFORMANCE
# ═══════════════════════════════════════════════════════════════════════════

"""
Pourquoi SpooledTemporaryFile est plus rapide pour petits fichiers ?

MÉMOIRE RAM :
• Très rapide (nanosecondes)
• Limitée (quelques GB)
• Perdue si le programme crash

DISQUE :
• Plus lent (millisecondes)
• Beaucoup d'espace (TB)
• Persistant

SpooledTemporaryFile = Meilleur des 2 mondes !
"""

import time

# Benchmark : Mémoire vs Disque
def benchmark_comparison():
    iterations = 1000
    data = b'x' * 1000  # 1 KB
    
    # En mémoire (SpooledTemporaryFile avec grande limite)
    start = time.time()
    for _ in range(iterations):
        with tempfile.SpooledTemporaryFile(max_size=10*1024*1024) as tmp:
            tmp.write(data)
            tmp.seek(0)
            tmp.read()
    temps_memoire = time.time() - start
    
    # Sur disque (TemporaryFile classique)
    start = time.time()
    for _ in range(iterations):
        with tempfile.TemporaryFile() as tmp:
            tmp.write(data)
            tmp.seek(0)
            tmp.read()
    temps_disque = time.time() - start
    
    print(f"Mémoire : {temps_memoire:.3f}s")
    print(f"Disque  : {temps_disque:.3f}s")
    print(f"Gain    : {temps_disque/temps_memoire:.1f}x plus rapide")


# ═══════════════════════════════════════════════════════════════════════════
# [GUIDE] QUAND UTILISER QUOI ?
# ═══════════════════════════════════════════════════════════════════════════

"""
┌────────────────────────────┬─────────────────────────────────┐
│  Situation                 │  Choix recommandé               │
├────────────────────────────┼─────────────────────────────────┤
│  Toujours petit (<1 MB)    │  SpooledTemporaryFile           │
│                            │  (restera en mémoire)           │
├────────────────────────────┼─────────────────────────────────┤
│  Toujours gros (>10 MB)    │  TemporaryFile                  │
│                            │  (pas besoin d'optimisation)    │
├────────────────────────────┼─────────────────────────────────┤
│  Taille variable           │  SpooledTemporaryFile           │
│  (peut être petit ou gros) │  (s'adapte automatiquement)     │
├────────────────────────────┼─────────────────────────────────┤
│  Besoin d'un nom de        │  NamedTemporaryFile             │
│  fichier visible           │  (SpooledTemp n'a pas de nom)   │
└────────────────────────────┴─────────────────────────────────┘
"""

# Spécifier le suffixe et préfixe
with tempfile.TemporaryFile(suffix='.txt', prefix='myapp_') as tmp:
    tmp.write(b'Content')
    # Nom ressemble à: myapp_abc123.txt (invisible dans filesystem)

# Spécifier le répertoire
with tempfile.TemporaryFile(dir='/tmp/custom') as tmp:
    tmp.write(b'Data')

# Sans context manager (pas recommandé)
tmp = tempfile.TemporaryFile()
tmp.write(b'Hello')
tmp.seek(0)
print(tmp.read())
tmp.close()  # IMPORTANT: fermer manuellement

# Utilisation avancée
with tempfile.TemporaryFile(
    mode='w+t',
    encoding='utf-8',
    newline='',  # Contrôle des fins de ligne
    suffix='.log',
    prefix='app_',
    dir='/var/tmp'
) as tmp:
    tmp.write('Log entry\n')
    tmp.seek(0)
    data = tmp.read()


[OK] TEMPFILE.NAMEDTEMPORARYFILE - FICHIER AVEC NOM

# Crée un fichier temporaire avec un nom visible dans le système
# Permet l'accès par d'autres processus

import tempfile

# Suppression automatique (défaut)
with tempfile.NamedTemporaryFile() as tmp:
    print(f"Fichier créé: {tmp.name}")
    # Exemple: /tmp/tmpabc123xyz
    
    tmp.write(b'Hello')
    tmp.flush()  # Important pour forcer l'écriture
    
    # Autre processus peut lire le fichier
    with open(tmp.name, 'rb') as f:
        print(f.read())
# Fichier supprimé automatiquement

# Garder le fichier après fermeture
with tempfile.NamedTemporaryFile(delete=False) as tmp:
    tmp.write(b'Persistent data')
    temp_path = tmp.name
    print(f"Fichier: {temp_path}")

# Fichier existe toujours
print(f"Existe: {os.path.exists(temp_path)}")  # True

# Supprimer manuellement
os.unlink(temp_path)

# Mode texte
with tempfile.NamedTemporaryFile(mode='w+t', delete=False) as tmp:
    tmp.write('Text content\n')
    tmp_path = tmp.name

# Utilisation pour passer des données à un programme externe
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as tmp:
    import json
    json.dump({'key': 'value'}, tmp)
    tmp_path = tmp.name

# Exécuter programme externe
import subprocess
subprocess.run(['some_program', '--input', tmp_path])

# Nettoyer
os.unlink(tmp_path)

# Spécifier tous les paramètres
with tempfile.NamedTemporaryFile(
    mode='w+b',
    suffix='.dat',
    prefix='data_',
    dir='/tmp',
    delete=True,  # Suppression auto (défaut)
    delete_on_close=True  # Python 3.12+
) as tmp:
    print(tmp.name)
    tmp.write(b'Binary data')

# Utilisation avec delete_on_close (Python 3.12+)
# Permet de fermer le fichier mais le garder sur disque
with tempfile.NamedTemporaryFile(delete_on_close=False) as tmp:
    tmp.write(b'Data')
    path = tmp.name
    # Fichier fermé mais existe toujours
# Supprimer après utilisation
os.unlink(path)


[OK] TEMPFILE.MKSTEMP - FICHIER TEMPORAIRE BAS NIVEAU

# Crée un fichier temporaire et retourne un descripteur de fichier
# Plus de contrôle mais nécessite gestion manuelle

import tempfile
import os

# Retourne: (file_descriptor, path)
fd, path = tempfile.mkstemp()

try:
    print(f"Descripteur: {fd}")
    print(f"Chemin: {path}")
    
    # Écrire avec os.write (bas niveau)
    os.write(fd, b'Hello World')
    
    # Ou convertir en objet fichier
    with os.fdopen(fd, 'w+b') as f:
        f.write(b'\nMore data')
        f.seek(0)
        print(f.read())
finally:
    # IMPORTANT: Supprimer manuellement
    os.unlink(path)

# Avec paramètres
fd, path = tempfile.mkstemp(
    suffix='.txt',
    prefix='temp_',
    dir='/tmp',
    text=False  # True pour mode texte (Windows)
)

# Utilisation recommandée
fd, path = tempfile.mkstemp()
try:
    with os.fdopen(fd, 'w') as f:
        f.write('Content')
    
    # Faire autre chose avec le fichier
    with open(path, 'r') as f:
        print(f.read())
finally:
    os.unlink(path)

# Exemple: créer fichier, fermer, puis rouvrir
fd, path = tempfile.mkstemp(suffix='.db')
os.close(fd)  # Fermer le descripteur

try:
    # Utiliser le fichier avec une bibliothèque
    import sqlite3
    conn = sqlite3.connect(path)
    conn.execute('CREATE TABLE test (id INTEGER)')
    conn.close()
finally:
    os.unlink(path)


[OK] TEMPFILE.TEMPORARYDIRECTORY - RÉPERTOIRE TEMPORAIRE

# Crée un répertoire temporaire complet
# Supprime automatiquement le répertoire et tout son contenu

import tempfile
import os

# Utilisation avec context manager (RECOMMANDÉ)
with tempfile.TemporaryDirectory() as tmpdir:
    print(f"Répertoire: {tmpdir}")
    
    # Créer des fichiers dedans
    file1 = os.path.join(tmpdir, 'file1.txt')
    file2 = os.path.join(tmpdir, 'file2.txt')
    
    with open(file1, 'w') as f:
        f.write('Content 1')
    
    with open(file2, 'w') as f:
        f.write('Content 2')
    
    # Créer sous-répertoires
    subdir = os.path.join(tmpdir, 'subdir')
    os.mkdir(subdir)
    
    # Lister contenu
    print(os.listdir(tmpdir))
# Tout supprimé automatiquement (récursif)

# Avec paramètres
with tempfile.TemporaryDirectory(
    suffix='_data',
    prefix='project_',
    dir='/tmp'
) as tmpdir:
    print(tmpdir)
    # Exemple: /tmp/project_abc123_data

# Sans context manager (pas recommandé)
tmpdir = tempfile.TemporaryDirectory()
print(tmpdir.name)
# Utiliser tmpdir.name
tmpdir.cleanup()  # Supprimer manuellement

# Exemple: extraction d'archive
import tempfile
import zipfile

with tempfile.TemporaryDirectory() as tmpdir:
    # Extraire archive
    with zipfile.ZipFile('archive.zip', 'r') as zip_ref:
        zip_ref.extractall(tmpdir)
    
    # Traiter les fichiers
    for root, dirs, files in os.walk(tmpdir):
        for file in files:
            path = os.path.join(root, file)
            print(f"Traitement: {path}")
# Archive et fichiers extraits supprimés

# Exemple: build temporaire
with tempfile.TemporaryDirectory() as tmpdir:
    # Copier sources
    import shutil
    shutil.copytree('src', os.path.join(tmpdir, 'src'))
    
    # Compiler
    build_dir = os.path.join(tmpdir, 'build')
    os.mkdir(build_dir)
    
    # Process...
    
    # Copier résultat final
    shutil.copy(
        os.path.join(build_dir, 'output.bin'),
        'dist/output.bin'
    )
# Fichiers temporaires nettoyés

# Gestion d'erreurs
with tempfile.TemporaryDirectory() as tmpdir:
    try:
        # Opérations risquées
        risky_operation(tmpdir)
    except Exception as e:
        print(f"Erreur: {e}")
        # Le répertoire sera quand même supprimé


[OK] TEMPFILE.MKDTEMP - RÉPERTOIRE TEMPORAIRE BAS NIVEAU

# Crée un répertoire temporaire et retourne son chemin
# Nécessite suppression manuelle

import tempfile
import shutil
import os

# Créer répertoire
tmpdir = tempfile.mkdtemp()
print(f"Répertoire: {tmpdir}")

try:
    # Utiliser le répertoire
    file_path = os.path.join(tmpdir, 'data.txt')
    with open(file_path, 'w') as f:
        f.write('Data')
    
    # Créer sous-structure
    os.makedirs(os.path.join(tmpdir, 'sub', 'deep'))
    
finally:
    # IMPORTANT: Supprimer récursivement
    shutil.rmtree(tmpdir)

# Avec paramètres
tmpdir = tempfile.mkdtemp(
    suffix='_cache',
    prefix='app_',
    dir='/var/tmp'
)

try:
    # Utilisation
    print(f"Cache: {tmpdir}")
finally:
    shutil.rmtree(tmpdir)

# Exemple: workspace temporaire
workspace = tempfile.mkdtemp(prefix='workspace_')

try:
    # Créer structure
    os.makedirs(os.path.join(workspace, 'input'))
    os.makedirs(os.path.join(workspace, 'output'))
    os.makedirs(os.path.join(workspace, 'temp'))
    
    # Traitement
    process_data(workspace)
    
finally:
    shutil.rmtree(workspace)


[OK] TEMPFILE.SPOOLEDTEMPORARYFILE - FICHIER EN MÉMOIRE/DISQUE

# Fichier temporaire qui reste en mémoire jusqu'à une certaine taille
# Bascule sur disque si trop gros
# Optimisation performance

import tempfile

# Défaut: 5 MB en mémoire
with tempfile.SpooledTemporaryFile() as tmp:
    tmp.write(b'Small data')  # En mémoire
    print(f"En mémoire: {not tmp._rolled}")  # True
    
    tmp.seek(0)
    print(tmp.read())

# Spécifier la taille maximum en mémoire
with tempfile.SpooledTemporaryFile(max_size=1024) as tmp:  # 1 KB
    # Écrire petit contenu
    tmp.write(b'x' * 500)  # En mémoire
    print(f"En mémoire: {not tmp._rolled}")  # True
    
    # Écrire plus que max_size
    tmp.write(b'y' * 600)  # Bascule sur disque
    print(f"Sur disque: {tmp._rolled}")  # True

# Mode texte
with tempfile.SpooledTemporaryFile(
    max_size=1024 * 1024,  # 1 MB
    mode='w+t',
    encoding='utf-8'
) as tmp:
    tmp.write('Text content\n' * 1000)
    tmp.seek(0)
    lines = tmp.readlines()

# Forcer l'écriture sur disque
with tempfile.SpooledTemporaryFile(max_size=1024) as tmp:
    tmp.write(b'Data')
    print(f"En mémoire: {not tmp._rolled}")
    
    # Forcer sur disque
    tmp.rollover()
    print(f"Sur disque: {tmp._rolled}")

# Exemple: traitement de gros fichiers
def process_large_file(input_path):
    with tempfile.SpooledTemporaryFile(max_size=10*1024*1024) as tmp:
        # Lire et transformer
        with open(input_path, 'rb') as f:
            for chunk in iter(lambda: f.read(8192), b''):
                processed = transform(chunk)
                tmp.write(processed)
        
        # Utiliser le résultat
        tmp.seek(0)
        return tmp.read()

# Tous les paramètres
with tempfile.SpooledTemporaryFile(
    max_size=5 * 1024 * 1024,  # 5 MB
    mode='w+b',
    buffering=-1,
    encoding=None,
    newline=None,
    suffix='.dat',
    prefix='spool_',
    dir='/tmp'
) as tmp:
    tmp.write(b'Data')


[OK] CONFIGURATION GLOBALE

import tempfile
import os

# Obtenir le répertoire temporaire par défaut
temp_dir = tempfile.gettempdir()
print(f"Répertoire temp: {temp_dir}")
# Linux/Mac: /tmp ou /var/tmp
# Windows: C:\Users\Username\AppData\Local\Temp

# Ordre de recherche du répertoire temporaire:
# 1. Variable TMPDIR (Unix) ou TEMP/TMP (Windows)
# 2. /tmp, /var/tmp, /usr/tmp (Unix)
# 3. Répertoire courant

# Obtenir le répertoire temporaire pour l'utilisateur
user_temp = tempfile.gettempdir()

# Changer temporairement le répertoire par défaut
original_tempdir = tempfile.tempdir
tempfile.tempdir = '/custom/temp/dir'

# Utiliser
with tempfile.TemporaryFile() as tmp:
    pass  # Créé dans /custom/temp/dir

# Restaurer
tempfile.tempdir = original_tempdir

# Vérifier les répertoires candidats
print(tempfile._candidate_tempdir_list())

# Définir via variable d'environnement
os.environ['TMPDIR'] = '/my/temp/dir'
print(tempfile.gettempdir())  # /my/temp/dir


[OK] GÉNÉRATION DE NOMS TEMPORAIRES

import tempfile

# Générer un nom de fichier temporaire unique
# (ne crée PAS le fichier)
temp_name = tempfile.mktemp()
print(temp_name)
# ATTENTION: Fonction obsolète et non sécurisée
# NE PAS UTILISER - race condition possible

# Préférer mkstemp ou NamedTemporaryFile

# Générer un préfixe unique
prefix = tempfile.gettempprefix()
print(prefix)  # 'tmp' par défaut

# Créer nom personnalisé sécurisé
fd, path = tempfile.mkstemp(
    suffix='.db',
    prefix='myapp_',
    dir=tempfile.gettempdir()
)
os.close(fd)
print(path)
# Exemple: /tmp/myapp_abc123xyz.db


[OK] PATTERNS D'UTILISATION COURANTS

import tempfile
import os
import shutil
import json

# === Pattern 1: Traitement de fichier ===

def process_file(input_path, output_path):
    """Traiter un fichier via fichier temporaire."""
    with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp:
        tmp_path = tmp.name
        
        # Traiter et écrire dans temp
        with open(input_path, 'r') as f:
            for line in f:
                processed = line.upper()
                tmp.write(processed)
    
    # Remplacer fichier original
    shutil.move(tmp_path, output_path)


# === Pattern 2: Atomique write ===

def atomic_write(path, content):
    """Écriture atomique via fichier temporaire."""
    dir_name = os.path.dirname(path)
    
    with tempfile.NamedTemporaryFile(
        mode='w',
        dir=dir_name,
        delete=False
    ) as tmp:
        tmp.write(content)
        tmp_path = tmp.name
    
    # Renommer atomiquement
    os.replace(tmp_path, path)


# === Pattern 3: Cache temporaire ===

class TempCache:
    def __init__(self):
        self.cache_dir = tempfile.mkdtemp(prefix='cache_')
    
    def get_path(self, key):
        return os.path.join(self.cache_dir, f"{key}.cache")
    
    def set(self, key, data):
        path = self.get_path(key)
        with open(path, 'wb') as f:
            f.write(data)
    
    def get(self, key):
        path = self.get_path(key)
        if os.path.exists(path):
            with open(path, 'rb') as f:
                return f.read()
        return None
    
    def cleanup(self):
        shutil.rmtree(self.cache_dir)
    
    def __enter__(self):
        return self
    
    def __exit__(self, *args):
        self.cleanup()

# Utilisation
with TempCache() as cache:
    cache.set('key1', b'data1')
    print(cache.get('key1'))


# === Pattern 4: Workspace temporaire ===

class TempWorkspace:
    def __init__(self):
        self.root = tempfile.mkdtemp(prefix='workspace_')
        self.input_dir = os.path.join(self.root, 'input')
        self.output_dir = os.path.join(self.root, 'output')
        self.temp_dir = os.path.join(self.root, 'temp')
        
        os.makedirs(self.input_dir)
        os.makedirs(self.output_dir)
        os.makedirs(self.temp_dir)
    
    def add_input(self, name, content):
        path = os.path.join(self.input_dir, name)
        with open(path, 'w') as f:
            f.write(content)
        return path
    
    def get_output(self, name):
        path = os.path.join(self.output_dir, name)
        if os.path.exists(path):
            with open(path, 'r') as f:
                return f.read()
        return None
    
    def cleanup(self):
        shutil.rmtree(self.root)
    
    def __enter__(self):
        return self
    
    def __exit__(self, *args):
        self.cleanup()


# === Pattern 5: Configuration temporaire ===

def with_temp_config(config_dict):
    """Context manager pour configuration temporaire."""
    with tempfile.NamedTemporaryFile(
        mode='w',
        suffix='.json',
        delete=False
    ) as tmp:
        json.dump(config_dict, tmp)
        config_path = tmp.name
    
    try:
        yield config_path
    finally:
        os.unlink(config_path)

# Utilisation
config = {'debug': True, 'port': 8000}
with with_temp_config(config) as config_path:
    # Utiliser config_path
    app.load_config(config_path)


# === Pattern 6: Tests avec fichiers temporaires ===

import unittest

class TestWithTempFiles(unittest.TestCase):
    def setUp(self):
        self.temp_dir = tempfile.mkdtemp()
    
    def tearDown(self):
        shutil.rmtree(self.temp_dir)
    
    def test_file_processing(self):
        # Créer fichier test
        test_file = os.path.join(self.temp_dir, 'test.txt')
        with open(test_file, 'w') as f:
            f.write('test content')
        
        # Tester
        result = process_function(test_file)
        self.assertEqual(result, expected)


# === Pattern 7: Download temporaire ===

import requests

def download_and_process(url):
    """Télécharger dans fichier temp et traiter."""
    with tempfile.NamedTemporaryFile(delete=False) as tmp:
        response = requests.get(url, stream=True)
        
        for chunk in response.iter_content(chunk_size=8192):
            tmp.write(chunk)
        
        tmp_path = tmp.name
    
    try:
        # Traiter le fichier téléchargé
        result = process_downloaded_file(tmp_path)
        return result
    finally:
        os.unlink(tmp_path)


# === Pattern 8: Pipeline de données ===

def data_pipeline(input_data):
    """Pipeline avec fichiers temporaires entre étapes."""
    with tempfile.TemporaryDirectory() as tmpdir:
        # Étape 1: Sauver input
        step1_output = os.path.join(tmpdir, 'step1.dat')
        with open(step1_output, 'wb') as f:
            f.write(transform_step1(input_data))
        
        # Étape 2: Traiter
        step2_output = os.path.join(tmpdir, 'step2.dat')
        with open(step1_output, 'rb') as f_in:
            with open(step2_output, 'wb') as f_out:
                f_out.write(transform_step2(f_in.read()))
        
        # Étape 3: Résultat final
        with open(step2_output, 'rb') as f:
            return transform_step3(f.read())


[OK] SÉCURITÉ

import tempfile
import os
import stat

# === Permissions sécurisées ===

# TemporaryFile/NamedTemporaryFile créent avec permissions 0600 (user only)
with tempfile.NamedTemporaryFile() as tmp:
    # Vérifier permissions
    perms = os.stat(tmp.name).st_mode
    print(f"Permissions: {oct(stat.S_IMODE(perms))}")  # 0o600

# mkstemp crée avec permissions 0600
fd, path = tempfile.mkstemp()
try:
    perms = os.stat(path).st_mode
    print(f"Permissions: {oct(stat.S_IMODE(perms))}")  # 0o600
finally:
    os.close(fd)
    os.unlink(path)

# TemporaryDirectory crée avec permissions 0700
with tempfile.TemporaryDirectory() as tmpdir:
    perms = os.stat(tmpdir).st_mode
    print(f"Permissions: {oct(stat.S_IMODE(perms))}")  # 0o700


# === Éviter les vulnérabilités ===

# [X] MAUVAIS: Race condition
temp_name = tempfile.mktemp()  # Obsolète!
with open(temp_name, 'w') as f:  # Vulnérable
    f.write('data')

# [OK] BON: Création atomique
with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp:
    tmp.write('data')
    temp_name = tmp.name


# [X] MAUVAIS: Permissions trop larges
fd, path = tempfile.mkstemp()
os.chmod(path, 0o666)  # Lisible par tous!

# [OK] BON: Garder permissions restrictives
fd, path = tempfile.mkstemp()
# Permissions 0600 par défaut


# === Nettoyage robuste ===

# [OK] BON: Context manager
with tempfile.TemporaryFile() as tmp:
    tmp.write(b'data')
# Nettoyage automatique même en cas d'exception

# [OK] BON: try/finally pour nettoyage manuel
fd, path = tempfile.mkstemp()
try:
    with os.fdopen(fd, 'w') as f:
        f.write('data')
    # Utiliser le fichier
finally:
    if os.path.exists(path):
        os.unlink(path)


[OK] PERFORMANCE

import tempfile
import time

# === SpooledTemporaryFile pour performance ===

# Petit fichier: reste en mémoire (rapide)
with tempfile.SpooledTemporaryFile(max_size=10*1024*1024) as tmp:
    tmp.write(b'x' * 1000)  # En mémoire
    # Opérations rapides

# Gros fichier: bascule sur disque automatiquement
with tempfile.SpooledTemporaryFile(max_size=1024) as tmp:
    tmp.write(b'x' * 10000)  # Sur disque
    # Toujours fonctionnel


# === Buffering ===

# Avec buffering (plus rapide pour beaucoup d'écritures)
with tempfile.NamedTemporaryFile(mode='wb', buffering=8192) as tmp:
    for i in range(10000):
        tmp.write(b'line\n')
    tmp.flush()  # Forcer l'écriture


# === Réutilisation du répertoire temporaire ===

temp_dir = tempfile.gettempdir()  # Calculé une fois

# Réutiliser
fd1, path1 = tempfile.mkstemp(dir=temp_dir)
fd2, path2 = tempfile.mkstemp(dir=temp_dir)


# === Benchmark ===

def benchmark_temp_methods():
    import time
    
    # TemporaryFile
    start = time.time()
    for i in range(1000):
        with tempfile.TemporaryFile() as tmp:
            tmp.write(b'data')
    print(f"TemporaryFile: {time.time() - start:.3f}s")
    
    # SpooledTemporaryFile
    start = time.time()
    for i in range(1000):
        with tempfile.SpooledTemporaryFile(max_size=1024) as tmp:
            tmp.write(b'data')
    print(f"SpooledTemporaryFile: {time.time() - start:.3f}s")


[OK] COMPATIBILITÉ MULTI-PLATEFORME

import tempfile
import os
import sys

# === Chemins portables ===

# Utiliser os.path.join pour construire chemins
with tempfile.TemporaryDirectory() as tmpdir:
    file_path = os.path.join(tmpdir, 'subdir', 'file.txt')
    
    # Créer répertoires parents si nécessaire
    os.makedirs(os.path.dirname(file_path), exist_ok=True)
    
    with open(file_path, 'w') as f:
        f.write('content')


# === Mode binaire vs texte ===

# Binaire: portable pour tous types de données
with tempfile.TemporaryFile(mode='wb') as tmp:
    tmp.write(b'binary data')

# Texte: spécifier l'encodage explicitement
with tempfile.TemporaryFile(mode='wt', encoding='utf-8') as tmp:
    tmp.write('text data')


# === Fins de ligne ===

# Mode texte avec newline='' pour contrôle total
with tempfile.NamedTemporaryFile(
    mode='wt',
    encoding='utf-8',
    newline=''  # Pas de conversion automatique
) as tmp:
    tmp.write('line1\nline2\n')  # \n préservé sur Windows


# === Répertoire temporaire selon OS ===

def get_appropriate_temp_dir():
    if sys.platform == 'win32':
        # Windows: utilise TEMP/TMP
        return tempfile.gettempdir()
    else:
        # Unix: préférer /tmp ou /var/tmp
        return tempfile.gettempdir()


[OK] GESTION D'ERREURS

import tempfile
import os
import shutil
import logging

# === Gestion robuste des erreurs ===

def safe_temp_operation():
    """Opération avec gestion d'erreurs complète."""
    tmpdir = None
    try:
        tmpdir = tempfile.mkdtemp()
        
        # Opérations risquées
        risky_file_operations(tmpdir)
        
        return True
        
    except OSError as e:
        logging.error(f"Erreur OS: {e}")
        return False
    
    except Exception as e:
        logging.error(f"Erreur inattendue: {e}")
        return False
    
    finally:
        # Nettoyage garanti
        if tmpdir and os.path.exists(tmpdir):
            try:
                shutil.rmtree(tmpdir)
            except Exception as e:
                logging.error(f"Erreur nettoyage: {e}")


# === Gestion permission denied ===

def handle_permission_errors():
    try:
        with tempfile.NamedTemporaryFile(dir='/root') as tmp:
            tmp.write(b'data')
    except PermissionError:
        # Fallback sur répertoire par défaut
        with tempfile.NamedTemporaryFile() as tmp:
            tmp.write(b'data')


# === Gestion disque plein ===

def handle_disk_full():
    try:
        with tempfile.TemporaryFile() as tmp:
            # Écrire beaucoup de données
            tmp.write(b'x' * (1024 ** 3))  # 1 GB
    except OSError as e:
        if e.errno == 28:  # ENOSPC - No space left on device
            logging.error("Disque plein!")
        else:
            raise


# === Retry avec backoff ===

import time

def create_temp_with_retry(max_retries=3):
    for attempt in range(max_retries):
        try:
            return tempfile.mkdtemp()
        except OSError as e:
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)  # Exponential backoff
                continue
            raise


# === Validation répertoire temporaire ===

def validate_temp_dir(temp_dir):
    """Valider que le répertoire temporaire est utilisable."""
    if not os.path.exists(temp_dir):
        raise ValueError(f"Répertoire n'existe pas: {temp_dir}")
    
    if not os.path.isdir(temp_dir):
        raise ValueError(f"N'est pas un répertoire: {temp_dir}")
    
    if not os.access(temp_dir, os.W_OK):
        raise PermissionError(f"Pas d'accès écriture: {temp_dir}")
    
    # Test création fichier
    try:
        fd, path = tempfile.mkstemp(dir=temp_dir)
        os.close(fd)
        os.unlink(path)
    except Exception as e:
        raise RuntimeError(f"Impossible créer fichier: {e}")


[OK] DÉBOGAGE

import tempfile
import os
import logging

# Configuration logging
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# === Tracer les créations/suppressions ===

class DebugTemporaryFile:
    """Wrapper pour déboguer TemporaryFile."""
    
    def __init__(self, *args, **kwargs):
        self.tmp = tempfile.NamedTemporaryFile(*args, **kwargs)
        logging.debug(f"Créé: {self.tmp.name}")
    
    def __enter__(self):
        return self.tmp
    
    def __exit__(self, *args):
        path = self.tmp.name
        result = self.tmp.__exit__(*args)
        logging.debug(f"Supprimé: {path}")
        return result
    
    def __getattr__(self, name):
        return getattr(self.tmp, name)


# Utilisation
with DebugTemporaryFile(mode='w') as tmp:
    tmp.write('debug content')


# === Afficher informations système ===

def debug_temp_info():
    """Afficher toutes les infos sur configuration temp."""
    print("=== Configuration Temporaire ===")
    print(f"Répertoire par défaut: {tempfile.gettempdir()}")
    print(f"Préfixe par défaut: {tempfile.gettempprefix()}")
    print(f"tempfile.tempdir: {tempfile.tempdir}")
    
    print("\n=== Variables d'environnement ===")
    for var in ['TMPDIR', 'TEMP', 'TMP']:
        value = os.environ.get(var, 'Non défini')
        print(f"{var}: {value}")
    
    print("\n=== Test création ===")
    with tempfile.NamedTemporaryFile() as tmp:
        print(f"Fichier test: {tmp.name}")
        perms = oct(os.stat(tmp.name).st_mode)[-3:]
        print(f"Permissions: {perms}")


# === Compter fichiers temporaires ===

def count_temp_files():
    """Compter fichiers dans répertoire temporaire."""
    temp_dir = tempfile.gettempdir()
    
    try:
        files = os.listdir(temp_dir)
        print(f"Fichiers dans {temp_dir}: {len(files)}")
        
        # Fichiers Python
        py_temp = [f for f in files if f.startswith('tmp')]
        print(f"Fichiers temp Python: {len(py_temp)}")
        
    except PermissionError:
        print("Permission refusée pour lister répertoire temp")


# === Logger détaillé ===

class VerboseTemporaryDirectory:
    """TemporaryDirectory avec logging détaillé."""
    
    def __init__(self, **kwargs):
        self.kwargs = kwargs
        self.tmpdir = None
    
    def __enter__(self):
        self.tmpdir = tempfile.mkdtemp(**self.kwargs)
        logging.info(f"Répertoire créé: {self.tmpdir}")
        return self.tmpdir
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if self.tmpdir:
            # Lister contenu avant suppression
            try:
                contents = list(os.walk(self.tmpdir))
                file_count = sum(len(files) for _, _, files in contents)
                dir_count = sum(len(dirs) for _, dirs, _ in contents)
                
                logging.info(
                    f"Nettoyage {self.tmpdir}: "
                    f"{file_count} fichiers, {dir_count} répertoires"
                )
                
                shutil.rmtree(self.tmpdir)
                logging.info(f"Supprimé: {self.tmpdir}")
                
            except Exception as e:
                logging.error(f"Erreur nettoyage: {e}")


[OK] INTÉGRATION AVEC D'AUTRES BIBLIOTHÈQUES

import tempfile
import os

# === Avec PIL/Pillow (images) ===

from PIL import Image

def process_image_with_temp(image_path):
    """Traiter image via fichier temporaire."""
    with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
        tmp_path = tmp.name
    
    try:
        # Ouvrir et modifier image
        img = Image.open(image_path)
        img = img.resize((800, 600))
        img.save(tmp_path)
        
        # Traitement supplémentaire
        return process_modified_image(tmp_path)
        
    finally:
        os.unlink(tmp_path)


# === Avec pandas (DataFrames) ===

import pandas as pd

def temp_csv_processing():
    """Traiter DataFrame via CSV temporaire."""
    df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
    
    with tempfile.NamedTemporaryFile(
        mode='w',
        suffix='.csv',
        delete=False
    ) as tmp:
        tmp_path = tmp.name
        df.to_csv(tmp_path, index=False)
    
    try:
        # Lire et traiter
        df_loaded = pd.read_csv(tmp_path)
        return df_loaded
    finally:
        os.unlink(tmp_path)


# === Avec sqlite3 ===

import sqlite3

def temp_database():
    """Créer base de données temporaire."""
    with tempfile.NamedTemporaryFile(
        suffix='.db',
        delete=False
    ) as tmp:
        db_path = tmp.name
    
    try:
        conn = sqlite3.connect(db_path)
        cursor = conn.cursor()
        
        cursor.execute('''
            CREATE TABLE users (
                id INTEGER PRIMARY KEY,
                name TEXT
            )
        ''')
        
        cursor.execute(
            "INSERT INTO users (name) VALUES (?)",
            ('Alice',)
        )
        
        conn.commit()
        
        # Requête
        cursor.execute("SELECT * FROM users")
        results = cursor.fetchall()
        
        conn.close()
        return results
        
    finally:
        if os.path.exists(db_path):
            os.unlink(db_path)


# === Avec zipfile ===

import zipfile

def create_temp_archive(files_dict):
    """Créer archive ZIP temporaire.
    
    Args:
        files_dict: dict {filename: content}
    """
    with tempfile.NamedTemporaryFile(
        suffix='.zip',
        delete=False
    ) as tmp:
        archive_path = tmp.name
    
    try:
        with zipfile.ZipFile(archive_path, 'w') as zf:
            for filename, content in files_dict.items():
                zf.writestr(filename, content)
        
        return archive_path
        
    except Exception:
        if os.path.exists(archive_path):
            os.unlink(archive_path)
        raise


# === Avec subprocess ===

import subprocess

def run_with_temp_input(command, input_data):
    """Exécuter commande avec input temporaire."""
    with tempfile.NamedTemporaryFile(
        mode='w',
        delete=False
    ) as tmp:
        tmp.write(input_data)
        tmp_path = tmp.name
    
    try:
        result = subprocess.run(
            [command, tmp_path],
            capture_output=True,
            text=True
        )
        return result.stdout
        
    finally:
        os.unlink(tmp_path)


# === Avec requests (téléchargements) ===

import requests

def download_to_temp(url):
    """Télécharger fichier dans temp."""
    response = requests.get(url, stream=True)
    response.raise_for_status()
    
    # Détecter extension depuis URL ou Content-Type
    extension = os.path.splitext(url)[1] or '.bin'
    
    with tempfile.NamedTemporaryFile(
        suffix=extension,
        delete=False
    ) as tmp:
        for chunk in response.iter_content(chunk_size=8192):
            tmp.write(chunk)
        
        return tmp.name


# === Avec json ===

import json

def temp_json_config(config_dict):
    """Créer fichier config JSON temporaire."""
    with tempfile.NamedTemporaryFile(
        mode='w',
        suffix='.json',
        delete=False
    ) as tmp:
        json.dump(config_dict, tmp, indent=2)
        return tmp.name


# === Avec csv ===

import csv

def temp_csv_writer(rows):
    """Écrire CSV dans fichier temporaire."""
    with tempfile.NamedTemporaryFile(
        mode='w',
        newline='',
        suffix='.csv',
        delete=False
    ) as tmp:
        writer = csv.writer(tmp)
        writer.writerows(rows)
        return tmp.name


[OK] EXEMPLES AVANCÉS

import tempfile
import os
import shutil
import hashlib

# === Exemple 1: Cache avec expiration ===

import time

class ExpiringTempCache:
    """Cache temporaire avec expiration."""
    
    def __init__(self, ttl=3600):
        self.cache_dir = tempfile.mkdtemp(prefix='cache_')
        self.ttl = ttl  # Time to live en secondes
    
    def _get_cache_path(self, key):
        key_hash = hashlib.md5(key.encode()).hexdigest()
        return os.path.join(self.cache_dir, f"{key_hash}.cache")
    
    def _get_meta_path(self, key):
        key_hash = hashlib.md5(key.encode()).hexdigest()
        return os.path.join(self.cache_dir, f"{key_hash}.meta")
    
    def set(self, key, value):
        cache_path = self._get_cache_path(key)
        meta_path = self._get_meta_path(key)
        
        # Écrire données
        with open(cache_path, 'wb') as f:
            f.write(value)
        
        # Écrire timestamp
        with open(meta_path, 'w') as f:
            f.write(str(time.time()))
    
    def get(self, key):
        cache_path = self._get_cache_path(key)
        meta_path = self._get_meta_path(key)
        
        if not os.path.exists(cache_path):
            return None
        
        # Vérifier expiration
        with open(meta_path, 'r') as f:
            timestamp = float(f.read())
        
        if time.time() - timestamp > self.ttl:
            # Expiré
            os.unlink(cache_path)
            os.unlink(meta_path)
            return None
        
        # Lire données
        with open(cache_path, 'rb') as f:
            return f.read()
    
    def cleanup(self):
        shutil.rmtree(self.cache_dir)
    
    def __enter__(self):
        return self
    
    def __exit__(self, *args):
        self.cleanup()


# === Exemple 2: Batch processor avec staging ===

class BatchProcessor:
    """Traitement par lots avec zone de staging temporaire."""
    
    def __init__(self, batch_size=100):
        self.batch_size = batch_size
        self.staging_dir = tempfile.mkdtemp(prefix='staging_')
        self.batch_count = 0
        self.current_batch = []
    
    def add_item(self, item):
        self.current_batch.append(item)
        
        if len(self.current_batch) >= self.batch_size:
            self._process_batch()
    
    def _process_batch(self):
        if not self.current_batch:
            return
        
        # Sauver batch dans fichier temporaire
        batch_file = os.path.join(
            self.staging_dir,
            f"batch_{self.batch_count:06d}.dat"
        )
        
        with open(batch_file, 'w') as f:
            for item in self.current_batch:
                f.write(f"{item}\n")
        
        self.batch_count += 1
        self.current_batch = []
    
    def finalize(self):
        # Traiter batch restant
        self._process_batch()
        
        # Traiter tous les batches
        results = []
        for batch_file in sorted(os.listdir(self.staging_dir)):
            path = os.path.join(self.staging_dir, batch_file)
            with open(path, 'r') as f:
                results.extend(f.read().splitlines())
        
        return results
    
    def cleanup(self):
        shutil.rmtree(self.staging_dir)
    
    def __enter__(self):
        return self
    
    def __exit__(self, *args):
        self.cleanup()


# === Exemple 3: Versionning de fichiers ===

class VersionedTempFile:
    """Fichier temporaire avec historique des versions."""
    
    def __init__(self):
        self.work_dir = tempfile.mkdtemp(prefix='versioned_')
        self.current_file = os.path.join(self.work_dir, 'current')
        self.versions_dir = os.path.join(self.work_dir, 'versions')
        os.makedirs(self.versions_dir)
        self.version = 0
    
    def write(self, content):
        # Sauvegarder version précédente
        if os.path.exists(self.current_file):
            version_file = os.path.join(
                self.versions_dir,
                f"v{self.version:04d}"
            )
            shutil.copy(self.current_file, version_file)
            self.version += 1
        
        # Écrire nouvelle version
        with open(self.current_file, 'w') as f:
            f.write(content)
    
    def read(self):
        if os.path.exists(self.current_file):
            with open(self.current_file, 'r') as f:
                return f.read()
        return None
    
    def get_version(self, version):
        version_file = os.path.join(
            self.versions_dir,
            f"v{version:04d}"
        )
        if os.path.exists(version_file):
            with open(version_file, 'r') as f:
                return f.read()
        return None
    
    def list_versions(self):
        return sorted(os.listdir(self.versions_dir))
    
    def cleanup(self):
        shutil.rmtree(self.work_dir)


# === Exemple 4: Transaction-like file operations ===

class FileTransaction:
    """Opérations sur fichiers avec rollback."""
    
    def __init__(self):
        self.backup_dir = tempfile.mkdtemp(prefix='transaction_')
        self.operations = []
    
    def write_file(self, path, content):
        # Backup si existe
        if os.path.exists(path):
            backup_path = os.path.join(
                self.backup_dir,
                os.path.basename(path) + '.bak'
            )
            shutil.copy(path, backup_path)
            self.operations.append(('modify', path, backup_path))
        else:
            self.operations.append(('create', path, None))
        
        # Écrire
        with open(path, 'w') as f:
            f.write(content)
    
    def delete_file(self, path):
        if os.path.exists(path):
            backup_path = os.path.join(
                self.backup_dir,
                os.path.basename(path) + '.bak'
            )
            shutil.copy(path, backup_path)
            self.operations.append(('delete', path, backup_path))
            os.unlink(path)
    
    def commit(self):
        # Supprimer backups
        shutil.rmtree(self.backup_dir)
        self.operations = []
    
    def rollback(self):
        # Restaurer fichiers
        for op_type, path, backup_path in reversed(self.operations):
            if op_type == 'modify':
                shutil.copy(backup_path, path)
            elif op_type == 'create':
                if os.path.exists(path):
                    os.unlink(path)
            elif op_type == 'delete':
                shutil.copy(backup_path, path)
        
        shutil.rmtree(self.backup_dir)
        self.operations = []
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is not None:
            self.rollback()
        else:
            self.commit()


# Utilisation
try:
    with FileTransaction() as tx:
        tx.write_file('file1.txt', 'content 1')
        tx.write_file('file2.txt', 'content 2')
        # Si exception: rollback automatique
        # Sinon: commit automatique
except Exception as e:
    print(f"Transaction annulée: {e}")


# === Exemple 5: Parallel processing avec temp files ===

import multiprocessing

def parallel_worker(data_chunk, output_path):
    """Worker pour traitement parallèle."""
    result = process_data(data_chunk)
    
    with open(output_path, 'w') as f:
        f.write(result)

def parallel_process_with_temp(data_list):
    """Traitement parallèle avec fichiers temp."""
    with tempfile.TemporaryDirectory() as tmpdir:
        # Créer tâches
        tasks = []
        output_files = []
        
        chunk_size = len(data_list) // multiprocessing.cpu_count()
        
        for i in range(0, len(data_list), chunk_size):
            chunk = data_list[i:i + chunk_size]
            output_path = os.path.join(tmpdir, f'result_{i}.txt')
            output_files.append(output_path)
            tasks.append((chunk, output_path))
        
        # Exécuter en parallèle
        with multiprocessing.Pool() as pool:
            pool.starmap(parallel_worker, tasks)
        
        # Combiner résultats
        results = []
        for output_file in output_files:
            with open(output_file, 'r') as f:
                results.append(f.read())
        
        return results


[OK] TESTING AVEC TEMPFILE

import unittest
import tempfile
import os
import shutil

# === Test unitaire basique ===

class TestFileProcessing(unittest.TestCase):
    
    def setUp(self):
        """Créer environnement de test."""
        self.test_dir = tempfile.mkdtemp()
    
    def tearDown(self):
        """Nettoyer après test."""
        shutil.rmtree(self.test_dir)
    
    def test_file_creation(self):
        """Tester création de fichier."""
        file_path = os.path.join(self.test_dir, 'test.txt')
        
        with open(file_path, 'w') as f:
            f.write('test content')
        
        self.assertTrue(os.path.exists(file_path))
        
        with open(file_path, 'r') as f:
            content = f.read()
        
        self.assertEqual(content, 'test content')
    
    def test_file_processing(self):
        """Tester traitement de fichier."""
        input_file = os.path.join(self.test_dir, 'input.txt')
        output_file = os.path.join(self.test_dir, 'output.txt')
        
        # Créer fichier input
        with open(input_file, 'w') as f:
            f.write('hello world')
        
        # Traiter
        process_file_function(input_file, output_file)
        
        # Vérifier output
        with open(output_file, 'r') as f:
            result = f.read()
        
        self.assertEqual(result, 'HELLO WORLD')


# === Test avec pytest et fixtures ===

import pytest

@pytest.fixture
def temp_dir():
    """Fixture pour répertoire temporaire."""
    tmpdir = tempfile.mkdtemp()
    yield tmpdir
    shutil.rmtree(tmpdir)

@pytest.fixture
def temp_file():
    """Fixture pour fichier temporaire."""
    fd, path = tempfile.mkstemp()
    os.close(fd)
    yield path
    os.unlink(path)

def test_with_temp_dir(temp_dir):
    """Test utilisant fixture temp_dir."""
    file_path = os.path.join(temp_dir, 'test.txt')
    
    with open(file_path, 'w') as f:
        f.write('content')
    
    assert os.path.exists(file_path)

def test_with_temp_file(temp_file):
    """Test utilisant fixture temp_file."""
    with open(temp_file, 'w') as f:
        f.write('test')
    
    with open(temp_file, 'r') as f:
        content = f.read()
    
    assert content == 'test'


# === Mock filesystem avec tempfile ===

class MockFilesystem:
    """Mock d'un filesystem pour tests."""
    
    def __init__(self):
        self.root = tempfile.mkdtemp()
    
    def create_file(self, relative_path, content=''):
        """Créer fichier dans mock filesystem."""
        full_path = os.path.join(self.root, relative_path)
        os.makedirs(os.path.dirname(full_path), exist_ok=True)
        
        with open(full_path, 'w') as f:
            f.write(content)
        
        return full_path
    
    def create_dir(self, relative_path):
        """Créer répertoire dans mock filesystem."""
        full_path = os.path.join(self.root, relative_path)
        os.makedirs(full_path, exist_ok=True)
        return full_path
    
    def get_path(self, relative_path):
        """Obtenir chemin complet."""
        return os.path.join(self.root, relative_path)
    
    def cleanup(self):
        """Nettoyer mock filesystem."""
        shutil.rmtree(self.root)
    
    def __enter__(self):
        return self
    
    def __exit__(self, *args):
        self.cleanup()


# Utilisation dans tests
def test_with_mock_filesystem():
    with MockFilesystem() as fs:
        # Créer structure
        fs.create_dir('project/src')
        fs.create_dir('project/tests')
        fs.create_file('project/src/main.py', 'print("hello")')
        fs.create_file('project/README.md', '# Project')
        
        # Tester fonction
        result = scan_project(fs.get_path('project'))
        
        assert 'main.py' in result
        assert 'README.md' in result


[OK] BONNES PRATIQUES

# === DO's (À FAIRE) ===

# [OK] Utiliser context managers
with tempfile.TemporaryFile() as tmp:
    tmp.write(b'data')
# Nettoyage automatique

# [OK] Spécifier le mode explicitement
with tempfile.NamedTemporaryFile(mode='w+t', encoding='utf-8') as tmp:
    tmp.write('text')

# [OK] Utiliser delete=False quand nécessaire
with tempfile.NamedTemporaryFile(delete=False) as tmp:
    path = tmp.name
# Fichier persiste pour utilisation ultérieure

# [OK] Flush avant lecture par autre processus
with tempfile.NamedTemporaryFile() as tmp:
    tmp.write(b'data')
    tmp.flush()  # Important!
    external_process(tmp.name)

# [OK] Gestion d'erreurs robuste
tmpdir = tempfile.mkdtemp()
try:
    # Opérations
    pass
finally:
    shutil.rmtree(tmpdir)

# [OK] Noms de fichiers descriptifs
with tempfile.NamedTemporaryFile(
    prefix='myapp_',
    suffix='.json'
) as tmp:
    pass

# [OK] Utiliser SpooledTemporaryFile pour optimisation
with tempfile.SpooledTemporaryFile(max_size=1024*1024) as tmp:
    tmp.write(small_data)  # Reste en mémoire


# === DON'Ts (À ÉVITER) ===

# [X] NE PAS utiliser mktemp (obsolète et dangereux)
path = tempfile.mktemp()  # VULNÉRABLE!

# [X] NE PAS oublier de fermer les descripteurs
fd, path = tempfile.mkstemp()
# Utiliser le fichier
# ... oubli de os.close(fd) = fuite de ressources

# [X] NE PAS oublier de supprimer avec delete=False
with tempfile.NamedTemporaryFile(delete=False) as tmp:
    path = tmp.name
# Fichier pas supprimé = fuite de disque

# [X] NE PAS modifier les permissions de manière non sécurisée
fd, path = tempfile.mkstemp()
os.chmod(path, 0o777)  # DANGEREUX!

# [X] NE PAS supposer que le chemin est portable
path = "/tmp/myfile.txt"  # Ne marche pas sur Windows

# [X] NE PAS ignorer les exceptions lors du nettoyage
try:
    shutil.rmtree(tmpdir)
except:
    pass  # Masque les problèmes

# [X] NE PAS réutiliser des noms de fichiers temporaires
with tempfile.NamedTemporaryFile() as tmp:
    path = tmp.name
# Fichier supprimé, path invalide

# [X] NE PAS mélanger modes binaire/texte
with tempfile.TemporaryFile(mode='wb') as tmp:
    tmp.write('text')  # ERREUR: bytes requis


[OK] CHECKLIST DE SÉCURITÉ

# === Vérifications essentielles ===

# [OK] Permissions restrictives (0600 pour fichiers, 0700 pour répertoires)
# [OK] Noms uniques (éviter mktemp)
# [OK] Création atomique
# [OK] Nettoyage garanti (context managers ou try/finally)
# [OK] Pas de données sensibles sans chiffrement
# [OK] Validation des chemins
# [OK] Pas de TOCTOU (Time-Of-Check-Time-Of-Use) races

# === Exemple sécurisé complet ===

def secure_temp_operation(sensitive_data):
    """Opération sécurisée avec données sensibles."""
    # Créer avec permissions restrictives
    fd, path = tempfile.mkstemp(suffix='.enc')
    
    try:
        # Vérifier permissions
        st = os.stat(path)
        mode = stat.S_IMODE(st.st_mode)
        if mode != 0o600:
            raise SecurityError("Permissions incorrectes")
        
        # Chiffrer données sensibles
        encrypted = encrypt_data(sensitive_data)
        
        # Écrire avec descripteur de fichier
        with os.fdopen(fd, 'wb') as f:
            f.write(encrypted)
        
        # Traiter
        result = process_encrypted_file(path)
        
        return result
        
    finally:
        # Supprimer de manière sécurisée
        if os.path.exists(path):
            # Écraser avec zéros (optionnel, selon besoin)
            with open(path, 'wb') as f:
                f.write(b'\x00' * os.path.getsize(path))
            
            os.unlink(path)


[OK] PERFORMANCE ET OPTIMISATION

import tempfile
import time
import os

# === Choix du bon outil selon le cas ===

# Petit fichier, accès fréquent -> SpooledTemporaryFile
with tempfile.SpooledTemporaryFile(max_size=1024*1024) as tmp:
    for _ in range(1000):
        tmp.write(b'data')
        tmp.seek(0)
        tmp.read()

# Fichier sans nom nécessaire -> TemporaryFile
with tempfile.TemporaryFile() as tmp:
    tmp.write(b'data')

# Besoin de partage entre processus -> NamedTemporaryFile
with tempfile.NamedTemporaryFile(delete=False) as tmp:
    tmp.write(b'data')
    path = tmp.name

subprocess.run(['other_program', path])
os.unlink(path)

# Plusieurs fichiers -> TemporaryDirectory
with tempfile.TemporaryDirectory() as tmpdir:
    for i in range(100):
        with open(os.path.join(tmpdir, f'file{i}.txt'), 'w') as f:
            f.write(f'content {i}')


# === Réutiliser le répertoire temporaire ===

# [X] Inefficace: créer à chaque fois
for i in range(100):
    with tempfile.TemporaryDirectory() as tmpdir:
        process(tmpdir)

# [OK] Efficace: créer une fois
with tempfile.TemporaryDirectory() as tmpdir:
    for i in range(100):
        subdir = os.path.join(tmpdir, f'iteration_{i}')
        os.makedirs(subdir)
        process(subdir)


# === Buffering optimal ===

# Petit fichier: pas de buffering spécial
with tempfile.TemporaryFile() as tmp:
    tmp.write(b'small data')

# Gros fichier: buffer adapté
with tempfile.TemporaryFile(buffering=8192*8) as tmp:
    for chunk in large_data_source():
        tmp.write(chunk)


# === Benchmark des différentes méthodes ===

def benchmark_temp_methods():
    iterations = 1000
    
    # TemporaryFile
    start = time.time()
    for _ in range(iterations):
        with tempfile.TemporaryFile() as tmp:
            tmp.write(b'x' * 1000)
    print(f"TemporaryFile: {time.time() - start:.3f}s")
    
    # NamedTemporaryFile
    start = time.time()
    for _ in range(iterations):
        with tempfile.NamedTemporaryFile() as tmp:
            tmp.write(b'x' * 1000)
    print(f"NamedTemporaryFile: {time.time() - start:.3f}s")
    
    # SpooledTemporaryFile (en mémoire)
    start = time.time()
    for _ in range(iterations):
        with tempfile.SpooledTemporaryFile(max_size=10000) as tmp:
            tmp.write(b'x' * 1000)
    print(f"SpooledTemporaryFile (mem): {time.time() - start:.3f}s")
    
    # mkstemp
    start = time.time()
    for _ in range(iterations):
        fd, path = tempfile.mkstemp()
        os.write(fd, b'x' * 1000)
        os.close(fd)
        os.unlink(path)
    print(f"mkstemp: {time.time() - start:.3f}s")


# === Pool de fichiers temporaires ===

class TempFilePool:
    """Pool de fichiers temporaires réutilisables."""
    
    def __init__(self, pool_size=10):
        self.pool_size = pool_size
        self.pool = []
        self.in_use = set()
        
        # Pré-créer fichiers
        for _ in range(pool_size):
            fd, path = tempfile.mkstemp()
            os.close(fd)
            self.pool.append(path)
    
    def acquire(self):
        """Obtenir un fichier du pool."""
        if not self.pool:
            # Pool vide, créer nouveau
            fd, path = tempfile.mkstemp()
            os.close(fd)
            self.in_use.add(path)
            return path
        
        path = self.pool.pop()
        self.in_use.add(path)
        return path
    
    def release(self, path):
        """Remettre fichier dans le pool."""
        if path in self.in_use:
            # Nettoyer fichier
            with open(path, 'wb') as f:
                f.truncate(0)
            
            self.in_use.remove(path)
            self.pool.append(path)
    
    def cleanup(self):
        """Supprimer tous les fichiers."""
        for path in self.pool:
            os.unlink(path)
        for path in self.in_use:
            os.unlink(path)
        
        self.pool.clear()
        self.in_use.clear()


[OK] ASTUCES ET TIPS

import tempfile
import os
import sys

# === Astuce 1: Nom personnalisé prévisible ===

def predictable_temp_name(base_name):
    """Créer nom temporaire avec partie prévisible."""
    temp_dir = tempfile.gettempdir()
    # Combine nom prévisible + suffixe unique
    fd, path = tempfile.mkstemp(
        prefix=f'{base_name}_',
        suffix='.tmp',
        dir=temp_dir
    )
    os.close(fd)
    return path

path = predictable_temp_name('myapp_config')
# Résultat: /tmp/myapp_config_abc123.tmp


# === Astuce 2: Context manager personnalisé ===

from contextlib import contextmanager

@contextmanager
def temporary_filename(suffix='', prefix='tmp', dir=None):
    """Context manager qui ne crée que le nom."""
    fd, path = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir)
    os.close(fd)
    os.unlink(path)  # Supprimer immédiatement
    
    try:
        yield path
    finally:
        if os.path.exists(path):
            os.unlink(path)

# Utilisation: obtenir un nom unique sans créer le fichier
with temporary_filename(suffix='.db') as db_path:
    # db_path existe comme nom mais pas comme fichier
    create_database(db_path)


# === Astuce 3: Copie sécurisée via temp ===

def safe_copy(src, dst):
    """Copie atomique via fichier temporaire."""
    dst_dir = os.path.dirname(dst)
    
    with tempfile.NamedTemporaryFile(
        dir=dst_dir,
        delete=False
    ) as tmp:
        with open(src, 'rb') as f_src:
            shutil.copyfileobj(f_src, tmp)
        tmp_path = tmp.name
    
    # Remplacer atomiquement
    os.replace(tmp_path, dst)


# === Astuce 4: Template de fichier temporaire ===

def create_temp_from_template(template_path, replacements):
    """Créer fichier temp depuis template avec substitutions."""
    with open(template_path, 'r') as f:
        template = f.read()
    
    # Substitutions
    for key, value in replacements.items():
        template = template.replace(f'{{{key}}}', value)
    
    # Écrire dans temp
    with tempfile.NamedTemporaryFile(
        mode='w',
        suffix='.cfg',
        delete=False
    ) as tmp:
        tmp.write(template)
        return tmp.name


# === Astuce 5: Rotation automatique de logs temporaires ===

class TempLogRotator:
    """Rotation automatique de logs temporaires."""
    
    def __init__(self, max_size=1024*1024, max_files=5):
        self.max_size = max_size
        self.max_files = max_files
        self.log_dir = tempfile.mkdtemp(prefix='logs_')
        self.current_log = self._new_log_file()
        self.log_count = 0
    
    def _new_log_file(self):
        path = os.path.join(self.log_dir, f'log_{self.log_count:04d}.txt')
        self.log_count += 1
        return open(path, 'w')
    
    def write(self, message):
        # Vérifier taille
        if self.current_log.tell() > self.max_size:
            self.current_log.close()
            self.current_log = self._new_log_file()
            
            # Supprimer vieux logs
            self._cleanup_old_logs()
        
        self.current_log.write(message + '\n')
        self.current_log.flush()
    
    def _cleanup_old_logs(self):
        logs = sorted(os.listdir(self.log_dir))
        while len(logs) > self.max_files:
            old_log = logs.pop(0)
            os.unlink(os.path.join(self.log_dir, old_log))
    
    def cleanup(self):
        self.current_log.close()
        shutil.rmtree(self.log_dir)


# === Astuce 6: Détection automatique du type ===

def smart_temp_file(data):
    """Créer fichier temp avec extension appropriée."""
    # Détecter type
    if isinstance(data, dict):
        suffix = '.json'
        import json
        content = json.dumps(data, indent=2)
        mode = 'w'
    elif isinstance(data, bytes):
        suffix = '.bin'
        content = data
        mode = 'wb'
    elif isinstance(data, str):
        suffix = '.txt'
        content = data
        mode = 'w'
    else:
        suffix = '.dat'
        content = str(data)
        mode = 'w'
    
    with tempfile.NamedTemporaryFile(
        mode=mode,
        suffix=suffix,
        delete=False
    ) as tmp:
        tmp.write(content)
        return tmp.name


# === Astuce 7: Temporary symlink ===

def temporary_symlink(target):
    """Créer symlink temporaire vers target."""
    tmp_dir = tempfile.mkdtemp()
    link_path = os.path.join(tmp_dir, 'link')
    
    try:
        os.symlink(target, link_path)
        yield link_path
    finally:
        if os.path.islink(link_path):
            os.unlink(link_path)
        os.rmdir(tmp_dir)


# === Astuce 8: Mesure de performances I/O ===

def measure_io_performance():
    """Mesurer performance I/O du système temp."""
    test_size = 100 * 1024 * 1024  # 100 MB
    data = b'x' * 1024  # 1 KB chunks
    
    # Test écriture
    start = time.time()
    with tempfile.TemporaryFile() as tmp:
        for _ in range(test_size // len(data)):
            tmp.write(data)
    write_time = time.time() - start
    write_speed = test_size / write_time / 1024 / 1024
    
    # Test lecture
    start = time.time()
    with tempfile.TemporaryFile() as tmp:
        tmp.write(b'x' * test_size)
        tmp.seek(0)
        while tmp.read(8192):
            pass
    read_time = time.time() - start
    read_speed = test_size / read_time / 1024 / 1024
    
    print(f"Écriture: {write_speed:.2f} MB/s")
    print(f"Lecture: {read_speed:.2f} MB/s")


# === Astuce 9: Compression automatique ===

import gzip

class CompressedTempFile:
    """Fichier temporaire avec compression automatique."""
    
    def __init__(self):
        self.tmp = tempfile.NamedTemporaryFile(
            suffix='.gz',
            delete=False
        )
        self.gz = gzip.GzipFile(fileobj=self.tmp, mode='wb')
        self.path = self.tmp.name
    
    def write(self, data):
        if isinstance(data, str):
            data = data.encode('utf-8')
        self.gz.write(data)
    
    def close(self):
        self.gz.close()
        self.tmp.close()
    
    def read_all(self):
        self.close()
        with gzip.open(self.path, 'rb') as f:
            return f.read()
    
    def cleanup(self):
        if os.path.exists(self.path):
            os.unlink(self.path)
    
    def __enter__(self):
        return self
    
    def __exit__(self, *args):
        self.close()
        self.cleanup()


# === Astuce 10: Debug helper ===

def temp_file_with_content_preview(content, max_preview=100):
    """Créer fichier temp et afficher aperçu."""
    with tempfile.NamedTemporaryFile(
        mode='w',
        delete=False
    ) as tmp:
        tmp.write(content)
        path = tmp.name
    
    preview = content[:max_preview]
    if len(content) > max_preview:
        preview += '...'
    
    print(f"Créé: {path}")
    print(f"Taille: {len(content)} chars")
    print(f"Aperçu: {preview}")
    
    return path


[OK] COMPARAISON: TEMPFILE VS ALTERNATIVES

# === tempfile vs /tmp direct ===

# [X] /tmp direct: non portable, non sécurisé
import os
path = '/tmp/myfile.txt'  # Windows: ne marche pas!
with open(path, 'w') as f:
    f.write('data')
os.unlink(path)

# [OK] tempfile: portable et sécurisé
with tempfile.NamedTemporaryFile(mode='w') as tmp:
    tmp.write('data')


# === tempfile vs io.BytesIO/StringIO ===

# BytesIO/StringIO: en mémoire uniquement
from io import BytesIO, StringIO

# En mémoire (limité par RAM)
buffer = BytesIO()
buffer.write(b'data')

# tempfile: peut gérer gros fichiers
with tempfile.TemporaryFile() as tmp:
    tmp.write(b'x' * (1024 ** 3))  # 1 GB OK


# === tempfile vs fichier permanent ===

# Fichier permanent: à gérer manuellement
import uuid
path = f'/tmp/file_{uuid.uuid4()}.txt'
try:
    with open(path, 'w') as f:
        f.write('data')
    # Utiliser
finally:
    if os.path.exists(path):
        os.unlink(path)

# tempfile: gestion automatique
with tempfile.NamedTemporaryFile(mode='w') as tmp:
    tmp.write('data')
    # Utiliser tmp.name


[OK] DÉPANNAGE (TROUBLESHOOTING)

import tempfile
import os

# === Problème 1: "No space left on device" ===

# Solution 1: Spécifier autre partition
try:
    with tempfile.TemporaryFile(dir='/var/tmp') as tmp:
        tmp.write(large_data)
except OSError as e:
    # Essayer /tmp
    with tempfile.TemporaryFile(dir='/tmp') as tmp:
        tmp.write(large_data)

# Solution 2: Nettoyer répertoire temp
temp_dir = tempfile.gettempdir()
# Supprimer vieux fichiers (faire attention!)


# === Problème 2: Permission denied ===

# Vérifier permissions du répertoire temp
temp_dir = tempfile.gettempdir()
if not os.access(temp_dir, os.W_OK):
    print(f"Pas d'accès écriture: {temp_dir}")
    
    # Utiliser répertoire alternatif
    alt_dir = os.path.expanduser('~/tmp')
    os.makedirs(alt_dir, exist_ok=True)
    
    with tempfile.TemporaryFile(dir=alt_dir) as tmp:
        tmp.write(b'data')


# === Problème 3: Fichier pas supprimé ===

# Cause: Exception avant sortie du context manager
try:
    with tempfile.NamedTemporaryFile(delete=False) as tmp:
        path = tmp.name
        tmp.write(b'data')
        raise Exception("Erreur!")
except Exception:
    # Fichier existe toujours
    if os.path.exists(path):
        os.unlink(path)


# === Problème 4: "Too many open files" ===

# Cause: descripteurs pas fermés
# [X] Mauvais
for i in range(10000):
    fd, path = tempfile.mkstemp()
    # Oubli de fermer fd!

# [OK] Bon
for i in range(10000):
    fd, path = tempfile.mkstemp()
    try:
        # Utiliser
        pass
    finally:
        os.close(fd)
        os.unlink(path)


# === Problème 5: Lenteur sur réseau ===

# Cause: répertoire temp sur montage réseau
# Vérifier
temp_dir = tempfile.gettempdir()
print(f"Répertoire temp: {temp_dir}")

# Forcer local
if is_network_path(temp_dir):
    local_tmp = '/tmp' if sys.platform != 'win32' else 'C:\\Temp'
    tempfile.tempdir = local_tmp


# === Problème 6: Caractères spéciaux dans noms ===

# Éviter caractères problématiques
with tempfile.NamedTemporaryFile(
    prefix='app_',  # Pas d'espaces, caractères spéciaux
    suffix='.txt'
) as tmp:
    pass


# === Problème 7: Encodage incorrect ===

# Toujours spécifier encodage en mode texte
with tempfile.TemporaryFile(mode='w+t', encoding='utf-8') as tmp:
    tmp.write('Caractères spéciaux: éàç 你好')
    tmp.seek(0)
    print(tmp.read())


# === Problème 8: Race condition lors du cleanup ===

# Cause: plusieurs processus utilisent même fichier
# Solution: utiliser NamedTemporaryFile avec delete=False
# et gérer cleanup manuellement avec verrous

import fcntl

def safe_cleanup_with_lock(path):
    lock_path = path + '.lock'
    
    with open(lock_path, 'w') as lock_file:
        fcntl.flock(lock_file, fcntl.LOCK_EX)
        
        if os.path.exists(path):
            os.unlink(path)
        
        fcntl.flock(lock_file, fcntl.LOCK_UN)
    
    os.unlink(lock_path)


[OK] FAQ (QUESTIONS FRÉQUENTES)

# Q1: Quelle est la différence entre TemporaryFile et NamedTemporaryFile?
"""
TemporaryFile: Pas de nom visible dans filesystem (plus sécurisé)
NamedTemporaryFile: A un nom visible (permet accès par autres processus)
"""

# Q2: Comment garder le fichier après fermeture?
with tempfile.NamedTemporaryFile(delete=False) as tmp:
    path = tmp.name
# Fichier persiste, supprimer manuellement: os.unlink(path)


# Q3: Comment passer un fichier temp à subprocess?
with tempfile.NamedTemporaryFile(delete=False) as tmp:
    tmp.write(b'input data')
    tmp.flush()
    path = tmp.name

subprocess.run(['program', path])
os.unlink(path)


# Q4: tempfile est-il thread-safe?
"""
Oui, tempfile génère des noms uniques de manière thread-safe.
Chaque thread peut créer des fichiers sans collision.
"""

# Q5: Peut-on changer le répertoire temporaire?
tempfile.tempdir = '/custom/temp'
# Ou via variable d'environnement TMPDIR (Unix) / TEMP (Windows)


# Q6: Comment créer plusieurs fichiers temp liés?
with tempfile.TemporaryDirectory() as tmpdir:
    file1 = os.path.join(tmpdir, 'file1.txt')
    file2 = os.path.join(tmpdir, 'file2.txt')
    # Créer fichiers...
# Tous supprimés ensemble


# Q7: tempfile marche avec pathlib?
from pathlib import Path

with tempfile.TemporaryDirectory() as tmpdir:
    path = Path(tmpdir) / 'subdir' / 'file.txt'
    path.parent.mkdir(parents=True)
    path.write_text('content')


# Q8: Comment tester le code utilisant tempfile?
"""
Utiliser tempfile directement dans les tests, c'est fait pour ça!
Pas besoin de mocker tempfile.
"""

# Q9: Y a-t-il une limite de taille?
"""
Non, limité seulement par l'espace disque disponible.
SpooledTemporaryFile a une limite avant de basculer sur disque.
"""

# Q10: Est-ce que tempfile nettoie au crash du programme?
"""
Non, les fichiers temporaires ne sont pas automatiquement supprimés
si le programme crash. Le système d'exploitation peut les nettoyer
au reboot ou via un service de nettoyage.
"""


[OK] RESSOURCES ET DOCUMENTATION

# Documentation officielle Python:
# https://docs.python.org/3/library/tempfile.html

# PEP (Python Enhancement Proposals):
# PEP 506: Adding A Secrets Module To The Standard Library

# Guides et tutoriels:
# - Real Python: Working With Files in Python
# - Python Module of the Week: tempfile

# Sécurité:
# - OWASP: Insecure Temporary File
# - CWE-377: Insecure Temporary File

# Bibliothèques alternatives/complémentaires:
# - pathlib: Manipulation de chemins orientée objet
# - shutil: Opérations de haut niveau sur fichiers
# - os: Opérations système de bas niveau

# Standards:
# - XDG Base Directory Specification (Linux)
# - Windows Known Folders


[OK] RÉCAPITULATIF RAPIDE

# === Choix rapide du bon outil ===

# Fichier simple, pas de nom nécessaire
tempfile.TemporaryFile()

# Fichier avec nom (partage avec subprocess)
tempfile.NamedTemporaryFile()

# Répertoire temporaire
tempfile.TemporaryDirectory()

# Fichier, contrôle bas niveau
tempfile.mkstemp()

# Répertoire, contrôle bas niveau
tempfile.mkdtemp()

# Petit fichier, optimisation mémoire
tempfile.SpooledTemporaryFile(max_size=1024*1024)

# === Pattern le plus courant ===
with tempfile.TemporaryDirectory() as tmpdir:
    # Travailler dans tmpdir
    work_file = os.path.join(tmpdir, 'work.txt')
    with open(work_file, 'w') as f:
        f.write('data')
    # Traiter...
# Auto-cleanup

# === Configuration globale ===
import tempfile
print(f"Répertoire temp: {tempfile.gettempdir()}")
print(f"Préfixe: {tempfile.gettempprefix()}")

# Changer temporairement
tempfile.tempdir = '/custom/temp'

# === Sécurité essentielle ===
# [OK] Toujours utiliser context managers
# [OK] Ne jamais utiliser mktemp() (obsolète)
# [OK] Vérifier permissions (0600/0700 par défaut)
# [OK] Nettoyer fichiers avec delete=False
# [OK] Protéger données sensibles

# === Erreurs courantes à éviter ===
# [X] Oublier de flush() avant accès externe
# [X] Ne pas fermer descripteurs de mkstemp()
# [X] Réutiliser chemins après suppression
# [X] Supposer que /tmp existe sur Windows
# [X] Mélanger modes binaire/texte


# === Template de code type ===

import tempfile
import os
import shutil

def typical_temp_workflow():
    """Workflow typique avec fichiers temporaires."""
    # Créer workspace temporaire
    with tempfile.TemporaryDirectory() as workspace:
        # Structure
        input_dir = os.path.join(workspace, 'input')
        output_dir = os.path.join(workspace, 'output')
        os.makedirs(input_dir)
        os.makedirs(output_dir)
        
        # Préparer données
        input_file = os.path.join(input_dir, 'data.txt')
        with open(input_file, 'w') as f:
            f.write('input data')
        
        # Traiter
        output_file = os.path.join(output_dir, 'result.txt')
        process_function(input_file, output_file)
        
        # Récupérer résultat
        with open(output_file, 'r') as f:
            result = f.read()
        
        return result
    # Workspace automatiquement nettoyé


# FIN DE LA CHEATSHEET
# Pour plus d'informations: https://docs.python.org/3/library/tempfile.html