[tool.ruff]
# Linting Python (équivalent d'ESLint côté crawler/geocoder).
line-length = 110
target-version = "py312"
exclude = [".git", "node_modules", "__pycache__"]

[tool.ruff.lint]
select = [
    "E",   # pycodestyle
    "F",   # pyflakes — imports/variables inutilisés, noms indéfinis
    "I",   # isort — ordre des imports
    "B",   # bugbear — pièges classiques (mutable default, boucle+except…)
    "UP",  # pyupgrade
    "S",   # bandit — analyse de sécurité (SQLi par f-string, subprocess, secrets…)
    "C4",  # comprehensions
]
ignore = [
    "E501",  # longueur gérée par le formateur
    "S101",  # `assert` : légitime dans les tests
]

[tool.ruff.lint.per-file-ignores]
# Les requêtes SQL sont construites avec des placeholders psycopg2 (%s) ;
# les rares f-strings ne portent que des noms de colonnes issus d'allowlists.
"apps/crawler/src/db.py" = ["S608"]
"apps/geocoder/src/*.py" = ["S608"]
"**/tests/*.py" = ["S105", "S106"]
# `random` sert au jitter de politesse entre deux requêtes HTTP, pas à générer
# des secrets : S311 (générateur non cryptographique) ne s'applique pas.
"apps/crawler/src/polite.py" = ["S311"]
"apps/geocoder/src/worker.py" = ["S311", "S608"]
# md5 sert d'empreinte de contenu pour détecter les pages inchangées,
# jamais de primitive de sécurité.
"apps/crawler/src/scraper_band.py" = ["S324"]

[tool.pytest.ini_options]
# Chaque app est testée depuis sa propre racine (`rootdir`), d'où le chemin relatif
# `src.` dans les imports de test. Voir le script `test:py` du package.json.
testpaths = ["tests"]
python_files = ["test_*.py"]
addopts = "-q --strict-markers"
