# Fichier: python_cheats/cheatsheets/Ruff.txt

```
# Cheatsheet Ruff - Guide Ultra-Détaillé pour Grands Débutants


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

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

# Imagine que tu écris du code Python
# Tu peux écrire du code de plein de manières différentes:

# Style 1 (mal formaté):
def hello(x,y):
  return x+y

# Style 2 (bien formaté):
def hello(x, y):
    return x + y

# Les deux fonctionnent, mais le style 2 est plus lisible!

# Problème: Comment savoir si ton code suit les bonnes pratiques?
# Solution: RUFF!

# RUFF = Outil ultra-rapide qui:
# 1. Vérifie ton code Python (linting)
# 2. Formate ton code automatiquement (formatting)
# 3. Détecte les bugs potentiels
# 4. Suggère des améliorations

# Pourquoi RUFF est différent?
# - Écrit en Rust (10-100x plus rapide que les autres outils)
# - Remplace plusieurs outils en un: Flake8, Black, isort, etc.
# - Configuration simple
# - Compatible avec tous les projets Python

# Analogie:
# Si ton code Python est un texte, Ruff est comme:
# - Un correcteur orthographique (détecte les erreurs)
# - Un correcteur grammatical (vérifie la syntaxe)
# - Un assistant de style (formate joliment)
# Tout ça en une seule commande, ultra-rapide!


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

# LINTER (Vérificateur)
# = Programme qui analyse ton code pour trouver:
#   - Erreurs potentielles
#   - Code non utilisé
#   - Mauvaises pratiques
#   - Problèmes de style
# Exemple: variables non utilisées, imports inutiles

# FORMATTER (Formateur)
# = Programme qui modifie automatiquement ton code pour:
#   - Ajouter/enlever des espaces
#   - Organiser les imports
#   - Respecter la longueur maximale des lignes
# Comme "Auto-format" dans un éditeur de texte

# RULE (Règle)
# = Une vérification spécifique que Ruff effectue
# Exemple de règles:
#   - E501: Ligne trop longue (>79 caractères)
#   - F401: Import non utilisé
#   - E302: Besoin de 2 lignes vides avant une fonction
# Chaque règle a un code unique (lettre + chiffres)

# RULE CATEGORY (Catégorie de règles)
# = Groupe de règles similaires
# Exemples de catégories:
#   - E: Erreurs de style (pycodestyle)
#   - F: Erreurs logiques (Pyflakes)
#   - I: Organisation des imports (isort)
#   - N: Convention de nommage (pep8-naming)
#   - UP: Modernisation du code (pyupgrade)

# CONFIGURATION FILE (Fichier de configuration)
# = Fichier où tu définis les règles à activer/désactiver
# Formats supportés:
#   - pyproject.toml (recommandé)
#   - ruff.toml
#   - .ruff.toml

# FIX (Correction automatique)
# = Ruff peut corriger automatiquement certaines erreurs
# Exemple: Supprimer un import non utilisé
# Avec l'option --fix

# IGNORE (Ignorer)
# = Dire à Ruff: "Ne vérifie pas cette règle"
# Utile pour des cas spéciaux

# SELECT (Sélectionner)
# = Activer certaines catégories de règles
# Par défaut, Ruff active seulement E et F


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

# 1. Tu as un fichier Python (exemple.py):
def hello(x,y):
  unused_var = 5
  return x+y

# 2. Tu lances Ruff:
ruff check exemple.py

# 3. Ruff analyse ton fichier et trouve:
#    - Pas d'espaces autour des arguments
#    - Variable non utilisée (unused_var)
#    - Indentation avec espaces (devrait être 4)

# 4. Ruff affiche les erreurs:
# exemple.py:1:11: E231 missing whitespace after ','
# exemple.py:2:3: F841 local variable 'unused_var' is assigned but never used

# 5. Tu peux corriger manuellement OU utiliser --fix:
ruff check --fix exemple.py

# 6. Ruff corrige automatiquement ce qu'il peut:
def hello(x, y):
    return x + y

# Variable non utilisée supprimée automatiquement!


# === DIFFÉRENCE AVEC D'AUTRES OUTILS ===

# AVANT RUFF (plusieurs outils):
# 1. black (formateur)
# 2. flake8 (linter)
# 3. isort (tri des imports)
# 4. pyupgrade (modernisation)
# 5. pylint (linter avancé)
# = 5 commandes différentes, lent, configurations multiples

# AVEC RUFF (un seul outil):
ruff check .        # Linting
ruff format .       # Formatting
# = 2 commandes, ultra-rapide, une seule configuration!

# Performance:
# flake8 + black + isort = ~5 secondes sur 100 fichiers
# ruff = ~0.1 seconde sur 100 fichiers
# = 50x plus rapide!


[OK] INSTALLATION SUPER DÉTAILLÉE

# === ÉTAPE 1: VÉRIFIER PYTHON ===

# Ruff nécessite Python 3.7+
# Vérifier la version:

python --version
# ou
python3 --version

# Doit afficher: Python 3.7.X ou plus récent

# Si Python n'est pas installé:
# - Windows: https://www.python.org/downloads/
# - macOS: brew install python3
# - Linux: sudo apt install python3


# === ÉTAPE 2: INSTALLER RUFF ===

# === MÉTHODE 1: pip (Recommandé pour débutants) ===

# Installation globale:
pip install ruff

# Ou avec Python 3 explicite:
pip3 install ruff

# Vérifier l'installation:
ruff --version

# Doit afficher: ruff 0.X.X (ou version plus récente)

# === MÉTHODE 2: pipx (Recommandé pour isolation) ===

# Pourquoi pipx?
# - Installe Ruff dans un environnement isolé
# - Pas de conflit avec d'autres packages
# - Recommandé pour les outils CLI

# Installer pipx d'abord:
pip install pipx
pipx ensurepath

# Puis installer Ruff:
pipx install ruff

# Vérifier:
ruff --version

# === MÉTHODE 3: uvx (Ultra-rapide, recommandé 2024+) ===

# uvx = Gestionnaire de packages ultra-rapide
# Installer uv d'abord:
pip install uv

# Lancer Ruff avec uvx (pas d'installation permanente):
uvx ruff check .

# Pourquoi uvx?
# - Pas d'installation nécessaire
# - Télécharge et lance Ruff à la volée
# - Toujours la dernière version
# - Idéal pour CI/CD

# === MÉTHODE 4: Homebrew (macOS/Linux) ===

brew install ruff

# Vérifier:
ruff --version

# === MÉTHODE 5: Cargo (depuis les sources Rust) ===

# Si tu as Rust installé:
cargo install ruff

# Vérifier:
ruff --version

# === MÉTHODE 6: Téléchargement manuel ===

# Windows:
# 1. Aller sur: https://github.com/astral-sh/ruff/releases
# 2. Télécharger ruff-x86_64-pc-windows-msvc.zip
# 3. Extraire ruff.exe
# 4. Ajouter au PATH

# macOS/Linux:
# 1. Télécharger le binaire depuis GitHub releases
# 2. chmod +x ruff
# 3. Déplacer dans /usr/local/bin/


# === ÉTAPE 3: VÉRIFICATION POST-INSTALLATION ===

# Vérifier que Ruff fonctionne:
ruff --help

# Doit afficher:
# An extremely fast Python linter and code formatter.
# 
# Usage: ruff [OPTIONS] <COMMAND>
# 
# Commands:
#   check   Run Ruff on the given files or directories
#   format  Format the given files or directories
#   ...

# Afficher la version:
ruff --version
# ruff 0.1.8

# Lister toutes les règles disponibles:
ruff rule --all
# Affiche toutes les règles (E, F, I, N, etc.)


# === ÉTAPE 4: INSTALLER DANS UN PROJET (Environnement virtuel) ===

# Créer un dossier de projet:
mkdir mon_projet && cd mon_projet

# Créer un environnement virtuel:
python -m venv venv

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

# Windows PowerShell:
venv\Scripts\Activate.ps1

# Windows CMD:
venv\Scripts\activate.bat

# Installer Ruff dans le venv:
pip install ruff

# Vérifier:
which ruff  # macOS/Linux
where ruff  # Windows

# Doit afficher le chemin dans ton venv


# === ÉTAPE 5: AJOUTER AU requirements.txt ===

# Si tu as un projet avec requirements.txt:
echo "ruff" >> requirements.txt

# Ou spécifier une version précise:
echo "ruff==0.1.8" >> requirements.txt

# Installer toutes les dépendances:
pip install -r requirements.txt


# === ÉTAPE 6: INTÉGRATION AVEC L'ÉDITEUR ===

# === VSCODE ===

# 1. Installer l'extension Ruff:
#    - Ouvre VSCode
#    - Clique sur Extensions (Ctrl+Shift+X)
#    - Cherche "Ruff"
#    - Installe "Ruff" par Astral Software

# 2. Configurer VSCode (settings.json):
{
  "[python]": {
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.fixAll": true,
      "source.organizeImports": true
    },
    "editor.defaultFormatter": "charliermarsh.ruff"
  }
}

# === PYCHARM ===

# 1. Installer le plugin Ruff:
#    - File > Settings > Plugins
#    - Chercher "Ruff"
#    - Installer

# 2. Configurer:
#    - File > Settings > Tools > Ruff
#    - Cocher "Enable Ruff"

# === VIM/NEOVIM ===

# Avec vim-plug:
Plug 'charliermarsh/ruff-lsp'

# Avec coc.nvim:
:CocInstall coc-ruff

# === SUBLIME TEXT ===

# Installer via Package Control:
# 1. Ctrl+Shift+P
# 2. Package Control: Install Package
# 3. Chercher "Ruff"


[OK] UTILISATION BASIQUE (PREMIERS PAS)

# === CRÉER UN FICHIER DE TEST ===

# Crée un fichier test.py avec ce contenu:

"""
test.py - Fichier pour tester Ruff
"""

import os
import sys
import math  # Import non utilisé

def hello(x,y):
    unused_var = 5
    result=x+y
    return result

def world( ):
    pass

class myclass:
    def __init__(self):
        self.value=10

if __name__=="__main__":
    print(hello(1,2))

# Ce fichier a BEAUCOUP de problèmes de style!


# === COMMANDE 1: ruff check (Vérifier) ===

# La commande de base:
ruff check test.py

# Affiche:
# test.py:4:1: F401 [*] `math` imported but unused
# test.py:6:11: E231 missing whitespace after ','
# test.py:7:5: F841 [*] Local variable `unused_var` is assigned to but never used
# test.py:8:11: E225 missing whitespace around operator
# test.py:11:11: E201 whitespace after '('
# test.py:14:7: N801 class name `myclass` should use CapWords convention
# test.py:16:20: E225 missing whitespace around operator
# test.py:18:7: E711 [*] comparison to `None` should be `cond is None`

# Explications des codes:
# F401 = Import non utilisé
# E231 = Manque d'espace après virgule
# F841 = Variable locale non utilisée
# E225 = Manque d'espace autour opérateur
# E201 = Espace après parenthèse ouvrante
# N801 = Nom de classe doit être en CapWords
# E711 = Comparaison à None avec == au lieu de is

# [*] = Peut être corrigé automatiquement avec --fix


# === COMMANDE 2: ruff check --fix (Corriger automatiquement) ===

ruff check --fix test.py

# Ruff modifie le fichier automatiquement:

"""
test.py - Fichier pour tester Ruff
"""

import os
import sys
# math supprimé automatiquement!

def hello(x, y):  # Espaces ajoutés
    # unused_var supprimée!
    result = x + y  # Espaces ajoutés
    return result

def world():  # Espace enlevé
    pass

class myclass:  # N801 pas corrigé (changement de nom risqué)
    def __init__(self):
        self.value = 10  # Espaces ajoutés

if __name__ == "__main__":  # Espaces ajoutés
    print(hello(1, 2))


# === COMMANDE 3: ruff check --diff (Voir les changements) ===

# Avant de corriger, voir ce qui sera changé:
ruff check --diff test.py

# Affiche un diff (comme git diff):
# --- test.py
# +++ test.py
# @@ -1,7 +1,6 @@
#  import os
#  import sys
# -import math
# 
# -def hello(x,y):
# +def hello(x, y):


# === COMMANDE 4: ruff format (Formater le code) ===

ruff format test.py

# Formate complètement le fichier (comme Black):
# - Indentation correcte
# - Espaces autour des opérateurs
# - Longueur des lignes (max 88 caractères par défaut)
# - Organisation des parenthèses

# Exemple résultat:

"""
test.py - Fichier pour tester Ruff
"""

import os
import sys


def hello(x, y):
    result = x + y
    return result


def world():
    pass


class myclass:
    def __init__(self):
        self.value = 10


if __name__ == "__main__":
    print(hello(1, 2))


# === COMMANDE 5: ruff check . (Vérifier tout le projet) ===

# Vérifie tous les fichiers Python dans le dossier courant:
ruff check .

# Vérifie récursivement (sous-dossiers):
ruff check .

# Par défaut, Ruff cherche:
# - *.py
# - *.pyi (type stubs)
# Ignore automatiquement:
# - venv/
# - .venv/
# - __pycache__/
# - .git/


# === COMMANDE 6: ruff check --watch (Mode surveillance) ===

# Surveille les fichiers et vérifie automatiquement à chaque sauvegarde:
ruff check --watch .

# Utile pendant le développement!
# Ctrl+C pour arrêter


# === COMMANDE 7: ruff check --select (Choisir des règles spécifiques) ===

# Vérifier seulement les imports non utilisés (F401):
ruff check --select F401 test.py

# Vérifier plusieurs règles:
ruff check --select E,F test.py
# E = Erreurs de style (pycodestyle)
# F = Erreurs logiques (Pyflakes)

# Vérifier toutes les règles possibles:
ruff check --select ALL test.py


# === COMMANDE 8: ruff check --ignore (Ignorer des règles) ===

# Ignorer les lignes trop longues (E501):
ruff check --ignore E501 test.py

# Ignorer plusieurs règles:
ruff check --ignore E501,F401 test.py


# === COMMANDE 9: ruff check --statistics (Statistiques) ===

# Afficher un résumé des erreurs:
ruff check --statistics test.py

# Affiche:
# 5  E231  missing whitespace after ','
# 3  F401  `math` imported but unused
# 2  E225  missing whitespace around operator
# 1  N801  class name should use CapWords convention


# === COMMANDE 10: ruff check --output-format (Format de sortie) ===

# Format JSON (utile pour CI/CD):
ruff check --output-format json test.py

# Format GitHub (pour GitHub Actions):
ruff check --output-format github test.py

# Format GitLab:
ruff check --output-format gitlab test.py


[OK] CONFIGURATION DÉTAILLÉE

# === FICHIER DE CONFIGURATION: pyproject.toml ===

# Ruff lit automatiquement pyproject.toml à la racine du projet

# Créer pyproject.toml:

[tool.ruff]
# Version Python cible
target-version = "py39"

# Longueur maximale des lignes
line-length = 88

# Fichiers/dossiers à exclure
exclude = [
    ".git",
    "__pycache__",
    ".venv",
    "venv",
    "build",
    "dist",
    "*.egg-info",
]

# === SÉLECTION DES RÈGLES ===

[tool.ruff.lint]
# Règles à activer
select = [
    "E",   # pycodestyle errors
    "W",   # pycodestyle warnings
    "F",   # Pyflakes
    "I",   # isort
    "N",   # pep8-naming
    "UP",  # pyupgrade
    "B",   # flake8-bugbear
    "C4",  # flake8-comprehensions
]

# Règles à ignorer
ignore = [
    "E501",  # Line too long (laisse Black gérer)
    "B008",  # Do not perform function calls in argument defaults
]

# Correction automatique activée par défaut
fix = true

# Afficher les corrections appliquées
show-fixes = true

# === CONFIGURATION PAR FICHIER ===

[tool.ruff.lint.per-file-ignores]
# Ignorer certaines règles dans des fichiers spécifiques
"__init__.py" = ["F401"]  # Imports non utilisés OK dans __init__.py
"tests/*.py" = ["S101"]   # assert OK dans les tests


# === EXEMPLE COMPLET pyproject.toml ===

[tool.ruff]
target-version = "py39"
line-length = 88
indent-width = 4

[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B"]
ignore = ["E501"]
fix = true

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "lf"


# === ALTERNATIVE: ruff.toml ===

# Si tu préfères un fichier séparé:

# ruff.toml
target-version = "py39"
line-length = 88

[lint]
select = ["E", "F", "I"]
ignore = ["E501"]


# === ALTERNATIVE: .ruff.toml ===

# Fichier caché (commence par un point):

# .ruff.toml
target-version = "py39"
line-length = 100


[OK] CATÉGORIES DE RÈGLES DÉTAILLÉES

# === E: pycodestyle errors (Erreurs de style PEP 8) ===

# E1: Indentation
# E101: Indentation avec tabulations
# E111: Indentation n'est pas un multiple de 4

# E2: Espaces
# E201: Espace après '('
# E202: Espace avant ')'
# E203: Espace avant ':'
# E231: Manque espace après ','

# E3: Lignes vides
# E301: Ligne vide attendue
# E302: 2 lignes vides attendues avant fonction
# E303: Trop de lignes vides

# E4: Imports
# E401: Plusieurs imports sur une ligne

# E5: Longueur de ligne
# E501: Ligne trop longue (>79 caractères)

# E7: Instructions
# E711: Comparaison à None avec == au lieu de is
# E712: Comparaison à True avec == au lieu de is

# Exemples:

# [X] E201: Espace après '('
result = hello( 1, 2)

# [OK] Correct:
result = hello(1, 2)

# [X] E231: Manque espace après ','
def hello(x,y):
    pass

# [OK] Correct:
def hello(x, y):
    pass

# [X] E711: Comparaison à None
if value == None:
    pass

# [OK] Correct:
if value is None:
    pass


# === W: pycodestyle warnings (Avertissements PEP 8) ===

# W2: Espaces
# W291: Espaces en fin de ligne
# W292: Pas de nouvelle ligne à la fin du fichier
# W293: Ligne vide contient des espaces

# W6: Déprécié
# W605: Séquence d'échappement invalide

# Exemples:

# [X] W291: Espace en fin de ligne
def hello():    
    pass

# [OK] Correct:
def hello():
    pass


# === F: Pyflakes (Erreurs logiques) ===

# F4: Imports
# F401: Import non utilisé
# F403: Import * non recommandé
# F405: Nom peut être undefined (import *)

# F8: Variables
# F821: Variable non définie
# F822: Variable non définie dans __all__
# F841: Variable locale assignée mais jamais utilisée

# Exemples:

# [X] F401: Import non utilisé
import math
import os  # Pas utilisé!

def hello():
    return math.sqrt(4)

# [OK] Correct (Ruff supprime os):
import math

def hello():
    return math.sqrt(4)

# [X] F841: Variable non utilisée
def hello():
    unused_var = 5  # Jamais utilisée!
    return 10

# [OK] Correct:
def hello():
    return 10

# [X] F821: Variable non définie
def hello():
    return undefined_var  # Erreur!

# [OK] Correct:
def hello():
    defined_var = 10
    return defined_var


# === I: isort (Organisation des imports) ===

# I001: Imports mal organisés
# I002: Import manquant dans __future__

# Ordre correct des imports (PEP 8):
# 1. Imports standard library
# 2. Imports tiers (pip install)
# 3. Imports locaux

# [X] I001: Imports désorganisés
import flask
import os
import sys
import requests

# [OK] Correct (Ruff réorganise):
import os
import sys

import flask
import requests

# Exemple complet:

# [X] Mal organisé:
from myapp import utils
import requests
import os
from flask import Flask

# [OK] Bien organisé:
import os

import requests
from flask import Flask

from myapp import utils


# === N: pep8-naming (Conventions de nommage) ===

# N8: Classes
# N801: Nom de classe doit être CapWords
# N802: Nom de fonction doit être lowercase
# N803: Nom d'argument doit être lowercase
# N806: Variable dans boucle doit être lowercase

# N8: Constantes
# N815: Variable dans class scope mixedCase
# N816: Variable dans module scope non uppercase

# Exemples:

# [X] N801: Nom de classe incorrect
class myclass:
    pass

# [OK] Correct:
class MyClass:
    pass

# [X] N802: Nom de fonction incorrect
def HelloWorld():
    pass

# [OK] Correct:
def hello_world():
    pass

# [X] N806: Variable de boucle incorrect
for X in range(10):
    print(X)

# [OK] Correct:
for x in range(10):
    print(x)

# [X] N816: Constante incorrecte
my_constant = 10

# [OK] Correct:
MY_CONSTANT = 10


# === UP: pyupgrade (Modernisation du code) ===

# Remplace le vieux code par du code moderne

# UP001-UP010: Type hints
# UP015: Redundant open mode
# UP018: Unnecessary string literal
# UP032: Use f-string instead of format

# Exemples:

# [X] UP032: Format ancien
name = "Alice"
message = "Hello {}".format(name)

# [OK] Correct (Ruff modernise):
name = "Alice"
message = f"Hello {name}"

# [X] UP015: Mode open() redondant
with open("file.txt", "r") as f:
    content = f.read()

# [OK] Correct (r est par défaut):
with open("file.txt") as f:
    content = f.read()

# [X] Vieux style type hints (Python 3.9+)
from typing import List, Dict

def hello(items: List[int]) -> Dict[str, int]:
    pass

# [OK] Moderne (Ruff modernise):
def hello(items: list[int]) -> dict[str, int]:
    pass


# === B: flake8-bugbear (Détection de bugs) ===

# B0: Design
# B001: Ne pas utiliser assert False
# B002: Argument par défaut mutable
# B003: Assigner à __slots__
# B006: Ne pas utiliser mutable default args

# B9: Autres bugs
# B901: return x in generator

# Exemples critiques:

# [X] B006: Argument par défaut mutable (BUG COMMUN!)
def append_to(item, list=[]):  # DANGER!
    list.append(item)
    return list

# Problème: list est partagée entre tous les appels!
print(append_to(1))  # [1]
print(append_to(2))  # [1, 2] au lieu de [2]!

# [OK] Correct:
def append_to(item, list=None):
    if list is None:
        list = []
    list.append(item)
    return list

# [X] B007: Variable de boucle non utilisée
for i in range(10):
    print("Hello")

# [OK] Correct:
for _ in range(10):
    print("Hello")


# === C4: flake8-comprehensions (Compréhensions) ===

# C400-C419: Optimisation des compréhensions

# Exemples:

# [X] C400: Liste inutile
values = list([1, 2, 3])

# [OK] Correct:
values = [1, 2, 3]

# [X] C416: Compréhension inutile
result = [x for x in range(10)]

# [OK] Correct:
result = list(range(10))


# === S: flake8-bandit (Sécurité) ===

# S1: assert
# S101: assert utilisé (vulnérable en production)

# S2: Exécution
# S102: exec() utilisé (dangereux)
# S301: pickle (dangereux)

# S6: Système
# S602: shell=True dans subprocess

# Exemples:

# [X] S101: assert en production
def divide(a, b):
    assert b != 0  # N'empêche pas l'erreur en -O
    return a / b

# [OK] Correct:
def divide(a, b):
    if b == 0:
        raise ValueError("Division by zero")
    return a / b

# [X] S602: shell=True dangereux
import subprocess
subprocess.call(f"rm {filename}", shell=True)  # Injection possible!

# [OK] Correct:
import subprocess
subprocess.call(["rm", filename])


# === D: pydocstyle (Docstrings) ===

# D1: Docstrings manquantes
# D100: Docstring manquante (module)
# D101: Docstring manquante (classe)
# D102: Docstring manquante (méthode)

# D2: Format docstring
# D200: Docstring sur une ligne
# D205: Ligne vide après résumé

# Exemples:

# [X] D101: Docstring manquante
class MyClass:
    def __init__(self):
        pass

# [OK] Correct:
class MyClass:
    """Une classe exemple."""
    
    def __init__(self):
        """Initialise MyClass."""
        pass

# [X] D205: Pas de ligne vide
def hello():
    """Fonction hello.
    Fait quelque chose.
    """
    pass

# [OK] Correct:
def hello():
    """Fonction hello.
    
    Fait quelque chose.
    """
    pass


# === AUTRES CATÉGORIES IMPORTANTES ===

# A: flake8-annotations (Type annotations)
# ANN001: Manque annotation argument
# ANN201: Manque annotation retour

# Q: flake8-quotes (Style des quotes)
# Q000: Utilise single quotes
# Q001: Utilise double quotes

# RET: flake8-return (Instructions return)
# RET501: Ne pas return None explicitement
# RET502: Ne pas return dans if sans else
# SIM: flake8-simplify (Simplification)
# SIM102: Utilise une seule condition
# SIM105: Utilise contextlib.suppress

# T: flake8-print (Détection print)
# T201: print() utilisé
# T203: pprint() utilisé


[OK] CORRECTION AUTOMATIQUE (--fix) DÉTAILLÉE

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

# Ruff peut corriger AUTOMATIQUEMENT certaines erreurs
# Tu n'as pas besoin de corriger manuellement!

# Commande:
ruff check --fix fichier.py

# Règles corrigeables automatiquement marquées [*]:
# F401 [*] Import non utilisé -> Suppression
# E231 [*] Espace manquant -> Ajout d'espace
# I001 [*] Imports désorganisés -> Réorganisation

# Règles NON corrigeables automatiquement:
# N801 Nom de classe incorrect -> Changement manuel risqué
# F821 Variable non définie -> Correction manuelle nécessaire


# === EXEMPLE COMPLET DE --fix ===

# Fichier avant (example.py):

import sys
import os
import math  # Non utilisé
import requests

def hello(x,y):
    unused_var = 5
    result=x+y
    return result

class myclass:
    def __init__(self):
        self.value=10

# Lancer --fix:
ruff check --fix example.py

# Fichier après:

import os
import sys

import requests

def hello(x, y):
    result = x + y
    return result

class myclass:  # N801 pas corrigé (risqué)
    def __init__(self):
        self.value = 10

# Changements automatiques:
# 1. math supprimé (F401)
# 2. Imports réorganisés (I001)
# 3. Espaces ajoutés (E231, E225)
# 4. unused_var supprimée (F841)


# === --fix AVEC --unsafe-fixes ===

# Certaines corrections sont "unsafe" (risquées)
# Par défaut, Ruff ne les applique pas

# Activer les corrections risquées:
ruff check --fix --unsafe-fixes example.py

# Exemple de correction risquée:

# Avant:
result = list(set([1, 2, 3, 1]))

# --fix normal: Pas de changement

# --fix --unsafe-fixes:
result = list({1, 2, 3, 1})  # set() littéral

# Pourquoi risqué?
# - Change la sémantique (peut changer l'ordre)
# - Peut casser des tests


# === --fix-only (Appliquer seulement les fixes) ===

# Corriger sans afficher les erreurs non corrigeables:
ruff check --fix-only example.py

# Utile dans des scripts automatisés


# === --diff (Voir les changements avant --fix) ===

# Voir ce qui sera changé AVANT de corriger:
ruff check --diff example.py

# Affiche un diff Git:
# --- example.py
# +++ example.py
# @@ -1,4 +1,3 @@
#  import sys
#  import os
# -import math
#  import requests

# Puis décider:
ruff check --fix example.py


[OK] FORMATEUR (ruff format) DÉTAILLÉ

# === QU'EST-CE QUE ruff format? ===

# ruff format = Outil de formatage automatique
# Équivalent à Black, mais intégré dans Ruff!

# Différence avec ruff check --fix:
# - ruff check --fix: Corrige les erreurs détectées
# - ruff format: Reformate TOUT le code pour cohérence

# ruff format s'occupe de:
# - Indentation (toujours 4 espaces)
# - Espaces autour des opérateurs
# - Longueur des lignes (wrapping automatique)
# - Organisation des parenthèses/crochets
# - Quotes (simple ou double)
# - Trailing commas


# === COMMANDE DE BASE ===

# Formater un fichier:
ruff format example.py

# Formater tout le projet:
ruff format .

# Mode dry-run (voir sans modifier):
ruff format --check example.py

# Afficher un diff:
ruff format --diff example.py


# === EXEMPLE COMPLET ===

# Fichier avant (messy.py):

def hello(x,y,z):
    result = x+y+z
    very_long_line = "this is a very very very very very very very very very long string that exceeds 88 characters"
    return result

data={'a':1,'b':2,'c':3}

# Lancer format:
ruff format messy.py

# Fichier après:

def hello(x, y, z):
    result = x + y + z
    very_long_line = (
        "this is a very very very very very very very very very long string"
        " that exceeds 88 characters"
    )
    return result


data = {"a": 1, "b": 2, "c": 3}

# Changements:
# 1. Espaces ajoutés partout
# 2. Ligne longue wrappée automatiquement
# 3. Dict formaté avec espaces


# === CONFIGURATION DU FORMATEUR ===

# Dans pyproject.toml:

[tool.ruff.format]
# Style des quotes
quote-style = "double"  # "double" ou "single"

# Style d'indentation
indent-style = "space"  # "space" ou "tab"

# Largeur d'indentation (nombre d'espaces)
indent-width = 4

# Fin de ligne
line-ending = "lf"  # "lf" (Unix) ou "crlf" (Windows) ou "auto"

# Respecter les magic trailing commas
skip-magic-trailing-comma = false

# Exemples:

# === quote-style ===

# quote-style = "double":
message = "Hello world"

# quote-style = "single":
message = 'Hello world'

# === indent-style ===

# indent-style = "space":
def hello():
    return 10

# indent-style = "tab":
def hello():
	return 10

# === skip-magic-trailing-comma ===

# skip-magic-trailing-comma = false (par défaut):
data = [
    1,
    2,
    3,  # Trailing comma gardée
]

# skip-magic-trailing-comma = true:
data = [1, 2, 3]  # Pas de trailing comma


# === DIFFÉRENCE ENTRE ruff check --fix ET ruff format ===

# ruff check --fix:
# - Corrige les erreurs spécifiques (imports, variables, etc)
# - Applique seulement les règles activées
# - Ne reformate pas TOUT

# ruff format:
# - Reformate complètement le code
# - Cohérence totale du style
# - Indépendant des règles de linting

# Workflow recommandé:
ruff check --fix .  # Corriger les erreurs
ruff format .       # Formater le code


[OK] INTÉGRATION CI/CD

# === GITHUB ACTIONS ===

# Fichier .github/workflows/ruff.yml:

name: Ruff

on: [push, pull_request]

jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install Ruff
        run: pip install ruff
      
      - name: Run Ruff check
        run: ruff check .
      
      - name: Run Ruff format
        run: ruff format --check .

# Explications:
# - runs-on: ubuntu-latest = Machine Linux
# - ruff check . = Vérifie tout le projet
# - ruff format --check . = Vérifie le formatage sans modifier


# === GITHUB ACTIONS (Version uvx, plus rapide) ===

name: Ruff

on: [push, pull_request]

jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install uv
        run: pip install uv
      
      - name: Run Ruff
        run: uvx ruff check .

# uvx télécharge et lance Ruff à la volée (plus rapide!)


# === GITLAB CI ===

# Fichier .gitlab-ci.yml:

ruff:
  image: python:3.11
  before_script:
    - pip install ruff
  script:
    - ruff check .
    - ruff format --check .

# Explications:
# - image: python:3.11 = Container Python
# - ruff check . = Linting
# - ruff format --check . = Formatage


# === PRE-COMMIT HOOKS ===

# Ruff peut s'intégrer avec pre-commit
# = Vérifie le code AVANT chaque commit Git

# Installer pre-commit:
pip install pre-commit

# Créer .pre-commit-config.yaml:

repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.1.8
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

# Installer les hooks:
pre-commit install

# Maintenant, à chaque git commit:
# 1. Ruff check --fix s'exécute automatiquement
# 2. Ruff format s'exécute automatiquement
# 3. Si des erreurs restent, le commit est bloqué!

# Test manuel:
pre-commit run --all-files


# === DOCKER ===

# Dockerfile avec Ruff:

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install -r requirements.txt
RUN pip install ruff

COPY . .

# Vérifier avec Ruff
RUN ruff check .
RUN ruff format --check .

CMD ["python", "app.py"]


# === MAKEFILE ===

# Simplifier les commandes avec un Makefile:

.PHONY: lint format check

lint:
	ruff check .

format:
	ruff format .

check:
	ruff check .
	ruff format --check .

fix:
	ruff check --fix .
	ruff format .

# Utilisation:
# make lint
# make format
# make fix


[OK] IGNORER DES ERREURS

# === IGNORER UNE RÈGLE SUR UNE LIGNE ===

# Syntaxe: # noqa: CODE

# Exemple:
import math  # noqa: F401
# F401 = Import non utilisé, ignoré sur cette ligne

result = x+y  # noqa: E225
# E225 = Manque espace, ignoré sur cette ligne

# Ignorer plusieurs règles:
import os, sys  # noqa: E401, F401

# Ignorer toutes les règles sur une ligne:
import math  # noqa


# === IGNORER UNE RÈGLE DANS TOUT LE FICHIER ===

# Au début du fichier:

# ruff: noqa: F401

import math
import os
# Tous les F401 ignorés dans ce fichier


# === IGNORER DANS LA CONFIGURATION ===

# Dans pyproject.toml:

[tool.ruff.lint]
ignore = [
    "E501",  # Ligne trop longue
    "F401",  # Import non utilisé
]

# Ignorer dans certains fichiers:

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]  # Imports OK dans __init__
"tests/*.py" = ["S101"]   # assert OK dans tests
"migrations/*.py" = ["E501"]  # Lignes longues OK


# === IGNORER UN DOSSIER ENTIER ===

[tool.ruff]
exclude = [
    "migrations/",
    "venv/",
    "build/",
]


# === EXEMPLE COMPLET ===

# Configuration:
[tool.ruff.lint]
ignore = ["E501"]

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
"tests/*.py" = ["S101"]

# Fichier Python:
import math  # noqa: F401
import os

def hello():
    assert True  # OK dans tests/*.py
    very_long_line = "..." * 100  # E501 ignoré globalement


[OK] RÈGLES PERSONNALISÉES

# === ACTIVER/DÉSACTIVER DES CATÉGORIES ===

# Dans pyproject.toml:

[tool.ruff.lint]
# Activer des catégories
select = [
    "E",   # pycodestyle errors
    "F",   # Pyflakes
    "I",   # isort
    "N",   # pep8-naming
    "UP",  # pyupgrade
    "B",   # flake8-bugbear
]

# Désactiver des règles spécifiques
ignore = [
    "E501",  # Ligne trop longue
    "N802",  # Fonction lowercase
]


# === ACTIVER TOUTES LES RÈGLES ===

[tool.ruff.lint]
select = ["ALL"]  # TOUT activer!

# Puis ignorer ce qui ne t'intéresse pas:
ignore = [
    "D",    # Docstrings (trop strict)
    "ANN",  # Annotations (trop strict)
]


# === CONFIGURATION PAR SÉVÉRITÉ ===

# Ruff n'a pas de notion de "warning" vs "error"
# Mais tu peux simuler avec plusieurs configs:

# Stricte (pour prod):
[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B"]

# Laxiste (pour dev):
[tool.ruff.lint]
select = ["E", "F"]
ignore = ["E501"]


# === RÈGLES EXPÉRIMENTALES ===

# Certaines règles sont en preview (bêta)
# Activer le mode preview:

[tool.ruff]
preview = true

[tool.ruff.lint]
select = ["ALL"]


[OK] DEBUGGING & DÉPANNAGE

# === PROBLÈME 1: "Ruff not found" ===

# Cause: Ruff pas installé ou pas dans PATH

# Solution 1: Vérifier installation
which ruff  # macOS/Linux
where ruff  # Windows

# Solution 2: Réinstaller
pip install --upgrade ruff

# Solution 3: Utiliser chemin complet
/usr/local/bin/ruff check .


# === PROBLÈME 2: "No Python files found" ===

# Cause: Ruff cherche dans le mauvais dossier

# Solution: Vérifier le dossier courant
pwd  # Affiche le dossier actuel

# Spécifier un chemin explicite:
ruff check /chemin/vers/projet


# === PROBLÈME 3: Configuration ignorée ===

# Cause: pyproject.toml mal placé ou mal formaté

# Solution 1: Vérifier l'emplacement
# pyproject.toml doit être à la RACINE du projet

# Solution 2: Vérifier la syntaxe
# Utiliser un validateur TOML en ligne

# Solution 3: Tester avec --config
ruff check --config /chemin/vers/pyproject.toml .


# === PROBLÈME 4: Trop d'erreurs! ===

# Solution 1: Commencer doucement
[tool.ruff.lint]
select = ["E", "F"]  # Seulement les erreurs critiques

# Solution 2: Corriger automatiquement
ruff check --fix .

# Solution 3: Ignorer temporairement
[tool.ruff.lint]
ignore = ["E501", "N801"]


# === PROBLÈME 5: Règle non reconnue ===

# Cause: Code de règle invalide ou plugin manquant

# Solution: Lister toutes les règles disponibles
ruff rule --all

# Chercher une règle spécifique:
ruff rule E501


# === PROBLÈME 6: Performance lente ===

# Cause: Trop de fichiers ou règles complexes

# Solution 1: Exclure des dossiers
[tool.ruff]
exclude = ["venv/", "build/", "*.pyc"]

# Solution 2: Désactiver des règles lourdes
[tool.ruff.lint]
ignore = ["D"]  # Docstrings (lent)

# Solution 3: Utiliser --statistics pour analyser
ruff check --statistics .


# === PROBLÈME 7: Conflit avec Black ===

# Solution: Ruff format remplace Black!
# Supprimer Black et utiliser:
ruff format .

# Si tu veux garder Black:
[tool.ruff.lint]
ignore = ["E501"]  # Laisser Black gérer


# === PROBLÈME 8: Conflit avec isort ===

# Solution: Ruff gère les imports automatiquement
[tool.ruff.lint]
select = ["I"]  # Activer isort dans Ruff

# Supprimer isort séparé


# === LOGS DE DEBUG ===

# Activer les logs détaillés:
ruff check --verbose .

# Afficher la configuration utilisée:
ruff check --show-settings .


[OK] EXEMPLES DE WORKFLOWS COMPLETS

# === WORKFLOW 1: Nouveau projet ===

# 1. Créer le projet
mkdir mon_projet && cd mon_projet
git init

# 2. Créer environnement virtuel
python -m venv venv
source venv/bin/activate  # Linux/macOS
venv\Scripts\activate     # Windows

# 3. Installer Ruff
pip install ruff

# 4. Créer pyproject.toml
cat > pyproject.toml << 'EOF'
[tool.ruff]
target-version = "py39"
line-length = 88

[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B"]
ignore = ["E501"]
fix = true

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
EOF

# 5. Créer .pre-commit-config.yaml
cat > .pre-commit-config.yaml << 'EOF'
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.1.8
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format
EOF

# 6. Installer pre-commit
pip install pre-commit
pre-commit install

# 7. Créer fichier Python
cat > app.py << 'EOF'
import os
import sys

def hello(name):
    return f"Hello {name}"

if __name__ == "__main__":
    print(hello("World"))
EOF

# 8. Vérifier avec Ruff
ruff check .
ruff format .

# 9. Commit
git add .
git commit -m "Initial commit"
# Pre-commit hooks s'exécutent automatiquement!


# === WORKFLOW 2: Projet existant ===

# 1. Aller dans le projet
cd mon_projet_existant

# 2. Installer Ruff
pip install ruff

# 3. Première vérification (voir les problèmes)
ruff check . > ruff_report.txt

# 4. Analyser le rapport
cat ruff_report.txt

# 5. Corriger automatiquement ce qui est possible
ruff check --fix .

# 6. Formater le code
ruff format .

# 7. Créer configuration adaptée
cat > pyproject.toml << 'EOF'
[tool.ruff]
target-version = "py39"

[tool.ruff.lint]
select = ["E", "F"]  # Commencer doucement
ignore = []

[tool.ruff.lint.per-file-ignores]
"tests/*.py" = ["S101"]
EOF

# 8. Vérifier à nouveau
ruff check .

# 9. Commit progressif
git add -p  # Commit par morceaux
git commit -m "Refactor: Apply Ruff fixes"


# === WORKFLOW 3: CI/CD ===

# 1. Créer GitHub Action
mkdir -p .github/workflows
cat > .github/workflows/ruff.yml << 'EOF'
name: Ruff

on: [push, pull_request]

jobs:
  ruff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install uv
        run: pip install uv
      - name: Run Ruff
        run: |
          uvx ruff check .
          uvx ruff format --check .
EOF

# 2. Commit et push
git add .github/workflows/ruff.yml
git commit -m "CI: Add Ruff checks"
git push

# 3. Vérifier sur GitHub
# Les checks s'exécutent automatiquement!


# === WORKFLOW 4: Migration depuis Black + Flake8 ===

# 1. Désinstaller les anciens outils
pip uninstall black flake8 isort

# 2. Installer Ruff
pip install ruff

# 3. Copier les configurations
# Black: line-length dans pyproject.toml
# Flake8: règles dans setup.cfg ou .flake8

# 4. Créer configuration Ruff équivalente
[tool.ruff]
line-length = 88  # Comme Black

[tool.ruff.lint]
select = ["E", "F", "I"]  # Flake8 + isort
ignore = ["E501"]  # Comme Black

# 5. Tester
ruff check .
ruff format .

# 6. Mettre à jour CI/CD
# Remplacer black && flake8 par ruff

# 7. Mettre à jour pre-commit
# Remplacer black et flake8 par ruff


[OK] ASTUCES & BONNES PRATIQUES

# === ASTUCE 1: Vérifier avant de commit ===

# Ajouter un alias Git:
git config --global alias.ruff '!ruff check . && ruff format --check .'

# Utilisation:
git ruff
git commit -m "..."


# === ASTUCE 2: Ignorer les fichiers générés ===

[tool.ruff]
exclude = [
    "migrations/",      # Django migrations
    "*_pb2.py",         # Protobuf
    "venv/",
    ".venv/",
    "build/",
    "dist/",
]


# === ASTUCE 3: Configuration par environnement ===

# Dev (laxiste):
[tool.ruff.lint]
select = ["E", "F"]

# Prod (stricte):
[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B", "S"]

# Utiliser des profils:
ruff check --config pyproject-dev.toml .
ruff check --config pyproject-prod.toml .


# === ASTUCE 4: Intégration VSCode parfaite ===

# settings.json:
{
  "[python]": {
    "editor.formatOnSave": true,
    "editor.codeActionsOnSave": {
      "source.fixAll": true,
      "source.organizeImports": true
    },
    "editor.defaultFormatter": "charliermarsh.ruff"
  },
  "ruff.lint.args": ["--config", "pyproject.toml"],
  "ruff.format.args": ["--config", "pyproject.toml"]
}


# === ASTUCE 5: Performance maximale ===

# Utiliser cache:
ruff check --cache-dir .ruff_cache .

# Parallélisation (activée par défaut):
# Ruff utilise tous les cores CPU automatiquement

# Mode watch pour dev:
ruff check --watch .


# === ASTUCE 6: Documentation automatique ===

# Générer un rapport HTML:
ruff check --output-format json . > ruff_report.json

# Puis utiliser un outil pour convertir en HTML


# === ASTUCE 7: Règles custom par équipe ===

# Créer un profil d'équipe:
[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B"]
ignore = ["E501", "N802"]

# Partager pyproject.toml dans le repo Git


[OK] COMPARAISON AVEC D'AUTRES OUTILS

# === RUFF vs BLACK ===

# Black:
# - Formateur seulement
# - Opinionated (pas configurable)
# - Écrit en Python (lent)

# Ruff format:
# - Formateur compatible Black
# - Plus rapide (10-100x)
# - Intégré avec linter

# Migration:
# pip uninstall black
# pip install ruff
# Remplacer: black . -> ruff format .


# === RUFF vs FLAKE8 ===

# Flake8:
# - Linter seulement
# - Plugins nombreux
# - Écrit en Python (lent)
# - Configuration complexe

# Ruff check:
# - Linter ultra-rapide
# - Plugins intégrés
# - Configuration simple
# - Correction automatique

# Migration:
# pip uninstall flake8
# pip install ruff
# Remplacer: flake8 . -> ruff check .


# === RUFF vs PYLINT ===

# Pylint:
# - Très complet
# - Très lent
# - Beaucoup de faux positifs

# Ruff:
# - Couvre 80% des règles Pylint
# - 100x plus rapide
# - Moins de faux positifs

# Combinaison possible:
# Ruff pour le quotidien
# Pylint pour analyse profonde mensuelle


# === RUFF vs ISORT ===

# isort:
# - Tri des imports seulement
# - Configurable

# Ruff (catégorie I):
# - Tri des imports intégré
# - Plus rapide
# - Correction automatique

# Migration:
# pip uninstall isort
# Activer dans Ruff:
[tool.ruff.lint]
select = ["I"]


# === TABLEAU COMPARATIF ===

# Outil      | Vitesse | Fonctions          | Config
# -----------|---------|--------------------|---------
# Black      | 1x      | Format             | Simple
# Flake8     | 1x      | Lint               | Moyenne
# isort      | 1x      | Imports            | Simple
# Pylint     | 0.1x    | Lint avancé        | Complexe
# Ruff       | 100x    | Lint + Format + Fix| Simple


[OK] RÈGLES AVANCÉES

# === RÈGLES DE SÉCURITÉ (S) ===

[tool.ruff.lint]
select = ["S"]

# S101: assert (vulnérable en -O)
# S102: exec() dangereux
# S103: chmod 777 (permissions trop ouvertes)
# S104: bind 0.0.0.0 (écoute sur toutes interfaces)
# S105: Mot de passe en dur
# S106: Mot de passe en argument
# S107: Mot de passe par défaut
# S301: pickle (dangereux)
# S302-S324: Fonctions dangereuses
# S501-S508: Crypto faible
# S601-S612: Shell injection
# S701-S703: Jinja2 injection

# Exemples:

# [X] S105: Mot de passe en dur
password = "secret123"  # DANGER!

# [OK] Correct:
import os
password = os.environ.get("PASSWORD")

# [X] S608: SQL injection
query = f"SELECT * FROM users WHERE name = '{user_input}'"

# [OK] Correct:
query = "SELECT * FROM users WHERE name = ?"
cursor.execute(query, (user_input,))


# === RÈGLES DE PERFORMANCE (PERF) ===

[tool.ruff.lint]
select = ["PERF"]

# PERF101: Utilise list comprehension au lieu de loop
# PERF102: Utilise dict comprehension
# PERF401: Évite list() dans loop

# Exemples:

# [X] PERF101: Boucle inutile
result = []
for x in range(10):
    result.append(x * 2)

# [OK] Correct:
result = [x * 2 for x in range(10)]


# === RÈGLES DE COMPLEXITÉ (C90) ===

[tool.ruff.lint]
select = ["C90"]

[tool.ruff.lint.mccabe]
max-complexity = 10

# Détecte les fonctions trop complexes

# Exemple:
def complex_function(x):
    if x > 0:
        if x < 10:
            if x % 2 == 0:
                if x != 6:
                    return "Valid"
    return "Invalid"

# C901: Complexité trop élevée (>10)


# === RÈGLES DE TYPE HINTS (ANN) ===

[tool.ruff.lint]
select = ["ANN"]

# ANN001: Argument sans type hint
# ANN201: Fonction sans type hint retour
# ANN204: __init__ sans type hint retour

# Exemples:

# [X] ANN001 + ANN201:
def hello(name):
    return f"Hello {name}"

# [OK] Correct:
def hello(name: str) -> str:
    return f"Hello {name}"


[OK] RUFF EN PRODUCTION

# === CONFIGURATION PRODUCTION RECOMMANDÉE ===

[tool.ruff]
target-version = "py39"
line-length = 88
indent-width = 4

[tool.ruff.lint]
select = [
    "E",    # pycodestyle errors
    "W",    # pycodestyle warnings
    "F",    # Pyflakes
    "I",    # isort
    "N",    # pep8-naming
    "UP",   # pyupgrade
    "B",    # flake8-bugbear
    "S",    # flake8-bandit
    "C4",   # flake8-comprehensions
    "RET",  # flake8-return
    "SIM",  # flake8-simplify
]

ignore = [
    "E501",  # Line too long (laisse format gérer)
    "S101",  # assert OK (géré par tests)
]

fix = true
show-fixes = true

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]
"tests/*.py" = ["S101", "S105"]
"migrations/*.py" = ["E501", "N806"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "lf"


# === SCRIPT DE VALIDATION CI/CD ===

#!/bin/bash
# validate.sh

set -e

echo "[RECHERCHE] Running Ruff lint..."
ruff check .

echo "* Running Ruff format check..."
ruff format --check .

echo "[GRAPHIQUE] Statistics..."
ruff check --statistics .

echo "