[project] name = "forge-engine" version = "0.1.0" description = "Forge Engine — editeur de jeux 2D/serious games sans code (Flask + Jinja2)." requires-python = ">=3.13" # --------------------------------------------------------------------------- # Ruff — linter + formatter Python (remplace flake8/isort/pyupgrade/black # pour la partie lint, un seul outil, tres rapide). Voir CODE_QUALITY.md # pour le detail de chaque famille de regles et pourquoi elle est activee. # --------------------------------------------------------------------------- [tool.ruff] line-length = 120 target-version = "py313" extend-exclude = [ "projects", # jeux crees par les utilisateurs (donnees runtime, gitignore) "user_assets", # "Mes assets" par compte (donnees runtime, gitignore) "data", # comptes/sessions (donnees runtime, sensible, gitignore) "Bug", # notes internes (gitignore) "regles", # notes internes (gitignore) ] [tool.ruff.lint] # Familles demandees explicitement : # F pyflakes (erreurs reelles : imports/variables inutilises, etc.) # E/W pycodestyle (style PEP 8) # B flake8-bugbear (pieges Python frequents, mutables par defaut...) # C4 flake8-comprehensions (comprehensions plus lisibles/rapides) # SIM flake8-simplify (simplifications de logique) # S flake8-bandit (regles de securite, integrees nativement a Ruff) # I (isort) ajoute en plus : Ruff remplace aussi isort, et une famille # lint sans tri d'imports coherent serait a moitie inutile ici. select = ["F", "E", "W", "B", "C4", "SIM", "S", "I"] # Aucune regle desactivee globalement (demande explicite : "configuration # strictement stricte partout"). Les exceptions ponctuelles se justifient # au cas par cas avec `# noqa: — ` dans le code lui-meme # (voir CODE_QUALITY.md, section "Justifier une exception"). ignore = [] [tool.ruff.lint.per-file-ignores] # Les tests utilisent `assert` (S101, attendu par pytest) et peuvent # construire des chemins/objets avec des valeurs "en dur" typiques de # fixtures (S106 faux positifs sur des mots de passe de test bidon) — # seule exception de PERIMETRE (jamais de regle eteinte pour du code # applicatif), documentee ici plutot que noyee sous des centaines de # `# noqa` repetitifs dans chaque fichier de test. "tests/**/*.py" = ["S101", "S106"] [tool.ruff.format] quote-style = "double" indent-style = "space" # --------------------------------------------------------------------------- # Mypy — typage statique, mode strict. Le code n'est aujourd'hui pas type # (projet historique) : ce mode reste active des maintenant (demande # explicite), le typage se fera progressivement fichier par fichier en # Phase 3, jamais en desactivant `strict` globalement pour "faire passer # le build". # --------------------------------------------------------------------------- [tool.mypy] python_version = "3.13" strict = true # `tests/` n'a pas de `__init__.py` (pytest s'en passe très bien) : sans # ces deux options, mypy hesite entre le module "test_x" (chemin relatif) # et "tests.test_x" (chemin depuis la racine) et refuse de demarrer # ("Source file found twice under different module names"). Ce reglage # fixe juste la resolution des chemins de module, ce n'est pas un # assouplissement de `strict`. explicit_package_bases = true mypy_path = "." exclude = [ "^projects/", "^user_assets/", "^data/", "^Bug/", "^regles/", ] # Bibliotheques tierces sans stubs (mutagen, qrcode, pyotp...) : mypy # --strict echoue sur un import sans annotations disponibles. On ignore # l'ABSENCE DE STUBS pour ces paquets precis (jamais nos propres modules), # une pratique standard documentee dans CODE_QUALITY.md — pas un # affaiblissement du mode strict sur notre code. [[tool.mypy.overrides]] module = ["mutagen.*", "qrcode.*", "pyotp.*"] ignore_missing_imports = true # --------------------------------------------------------------------------- # Vulture — code mort (fonctions/variables/imports jamais utilises). # --------------------------------------------------------------------------- [tool.vulture] min_confidence = 80 paths = [ "ai", "auth", "core", "db", "filters", "publish", "routes", "game_engine", "document_engine", "scripts", "app.py", "build_css.py", "vulture_whitelist.py", ] exclude = ["*/tests/*"] # Faux positifs structurels (routes Flask enregistrees par decorateur, # hooks appeles par le framework via une convention de nom, champs lus # dynamiquement) : whitelist DEDIEE, jamais un `min_confidence` abaisse # en general (voir vulture_whitelist.py + CODE_QUALITY.md). # --------------------------------------------------------------------------- # Bandit — scan de securite (independant des regles S de Ruff : Bandit # reste l'outil de reference historique, execute separement pour avoir un # rapport SARIF/JSON dedie exploitable par SonarQube). # --------------------------------------------------------------------------- [tool.bandit] exclude_dirs = ["projects", "user_assets", "data", "Bug", "regles", "tests"] # --------------------------------------------------------------------------- # import-linter — contrats d'architecture en couches. Reflete la # separation REELLE du projet (voir CODE_QUALITY.md pour le detail) : # db est la couche la plus basse (persistance), core est la "racine de # composition" qui cable Flask + les autres paquets, routes est la couche # HTTP tout en haut. `app.py` (point d'entree, un module et non un # paquet) n'est pas un "root package" valide pour import-linter — il # reste hors contrat, c'est lui qui importe `routes`/`core`, jamais # l'inverse, rien a verifier de ce cote. Un contrat CASSE aujourd'hui # (db -> core, voir db/connection.py) est un signal reel, jamais masque. # --------------------------------------------------------------------------- [tool.importlinter] root_packages = ["routes", "ai", "publish", "core", "game_engine", "document_engine", "auth", "filters", "db"] [[tool.importlinter.contracts]] name = "Couches applicatives Forge Engine" type = "layers" layers = [ "routes", "ai | publish | core", "game_engine | document_engine", "auth | filters", "db", ] # --------------------------------------------------------------------------- # djLint — lint/format des templates Jinja2. # --------------------------------------------------------------------------- [tool.djlint] profile = "jinja" max_line_length = 160 indent = 2 ignore_blocks = "raw" exclude = ".venv,projects,user_assets,data,Bug,regles"