Spaces:
Running
Running
| [build-system] | |
| requires = ["setuptools>=68.0", "wheel"] | |
| build-backend = "setuptools.build_meta" | |
| [project] | |
| name = "picarones" | |
| version = "1.0.0" | |
| description = "Plateforme de comparaison de moteurs OCR/HTR pour documents patrimoniaux" | |
| readme = "README.md" | |
| requires-python = ">=3.11" | |
| license = { text = "Apache-2.0" } | |
| authors = [{ name = "maribakulj" }] | |
| keywords = ["ocr", "htr", "patrimoine", "benchmark", "cer", "wer", "gallica", "escriptorium", "iiif"] | |
| classifiers = [ | |
| "Development Status :: 4 - Beta", | |
| "Programming Language :: Python :: 3.11", | |
| "Programming Language :: Python :: 3.12", | |
| "Programming Language :: Python :: 3.13", | |
| "License :: OSI Approved :: Apache Software License", | |
| "Operating System :: OS Independent", | |
| "Topic :: Scientific/Engineering :: Artificial Intelligence", | |
| "Topic :: Text Processing :: Linguistic", | |
| "Intended Audience :: Science/Research", | |
| "Natural Language :: French", | |
| ] | |
| dependencies = [ | |
| "click>=8.1.0", | |
| "jiwer>=3.0.0", | |
| "Pillow>=10.0.0", | |
| "pyyaml>=6.0.0", | |
| "pytesseract>=0.3.10", | |
| "tqdm>=4.66.0", | |
| "numpy>=1.24.0", | |
| "jinja2>=3.1.0", | |
| # XML parsing sécurisé contre les attaques XXE / Billion Laughs. | |
| # Utilisé par ``picarones.web.corpus_utils`` pour le parsing ALTO/PAGE | |
| # quand un utilisateur uploade un corpus XML. | |
| "defusedxml>=0.7.1", | |
| ] | |
| [project.urls] | |
| Homepage = "https://github.com/maribakulj/Picarones" | |
| Documentation = "https://github.com/maribakulj/Picarones/blob/main/INSTALL.md" | |
| Repository = "https://github.com/maribakulj/Picarones" | |
| Changelog = "https://github.com/maribakulj/Picarones/blob/main/CHANGELOG.md" | |
| "Bug Tracker" = "https://github.com/maribakulj/Picarones/issues" | |
| [project.optional-dependencies] | |
| # Développement et tests. | |
| # pytest-timeout (Sprint A1) garantit qu'aucun test individuel ne hang la CI | |
| # au-delà de la limite définie dans [tool.pytest.ini_options]. | |
| # mypy (Sprint A1, M-4) : type-check strict sur picarones/core/ + lax ailleurs. | |
| # bandit (Sprint A1, B-7) : scanner sécurité statique du code Python. | |
| # pip-audit (Sprint A1, B-7) : détection des CVE des dépendances installées. | |
| dev = [ | |
| "pytest>=7.4.0", | |
| "pytest-cov>=4.1.0", | |
| "pytest-timeout>=2.3.0", | |
| "httpx>=0.27.0", | |
| "fastapi>=0.111.0", | |
| "uvicorn[standard]>=0.29.0", | |
| "python-multipart>=0.0.9", | |
| "mypy>=1.10.0", | |
| "bandit>=1.7.0", | |
| "pip-audit>=2.7.0", | |
| ] | |
| # Interface web FastAPI | |
| web = ["fastapi>=0.111.0", "uvicorn[standard]>=0.29.0", "httpx>=0.27.0", "python-multipart>=0.0.9"] | |
| # Tests statistiques avancés (Wilcoxon exact, Friedman chi² exact, Nemenyi) | |
| # Sinon fallback pur Python (approximations normale / Wilson-Hilferty). | |
| stats = ["scipy>=1.11.0"] | |
| # Extracteurs d'entités nommées (Sprint 40 — A.II.1.a du plan d'évolution). | |
| # Sans cet extra, picarones.core.ner_backends.SpacyEntityExtractor tombe | |
| # en mode dégradé silencieux et le runner saute le calcul NER. | |
| ner = ["spacy>=3.7.0"] | |
| # Import HuggingFace Datasets | |
| hf = ["datasets>=2.19.0"] | |
| # Moteurs OCR optionnels | |
| pero = ["pero-ocr>=0.1.0"] | |
| kraken = ["kraken>=4.0.0"] | |
| # Adaptateurs LLM | |
| llm = [ | |
| "openai>=1.0.0", | |
| "anthropic>=0.20.0", | |
| "mistralai>=1.0.0", | |
| ] | |
| # OCR cloud APIs | |
| ocr-cloud = [ | |
| "google-cloud-vision>=3.0.0", | |
| "boto3>=1.34.0", | |
| "azure-ai-formrecognizer>=3.3.0", | |
| ] | |
| # Métriques philologiques pour documents historiques (Cercle 3, phase B | |
| # du chantier de refonte post-Sprint 97). Aujourd'hui les modules | |
| # philologiques (`picarones.extras.historical.*`) sont livrés dans le | |
| # package principal sans dépendance externe — l'extra ``[historical]`` | |
| # n'ajoute donc aucun paquet à installer. Il est déclaré ici pour | |
| # **documenter l'intention** : un usage purement moderne (sans cas | |
| # d'usage patrimonial) peut ignorer le sous-package extras/historical/ | |
| # entièrement, et un futur split en package PyPI séparé | |
| # ``picarones-historical`` réutilisera ce nom d'extra. | |
| historical = [] | |
| # Importeurs de corpus depuis sources distantes (Cercle 3, phase C). | |
| # Les 6 importeurs (sous extras/importers/, dotted | |
| # ``picarones.extras.importers.*``) sont livrés dans le package | |
| # principal. ``[importers]`` documente l'intention de séparation | |
| # future en package PyPI ``picarones-importers``. Les modules | |
| # ``huggingface`` et ``escriptorium`` émettent un ``UserWarning`` à | |
| # l'import (statut expérimental). | |
| importers = [] | |
| # Installation complète (tous les extras sauf les OCR cloud) | |
| all = [ | |
| "picarones[web,hf,llm,dev,historical,importers]", | |
| ] | |
| [project.scripts] | |
| picarones = "picarones.cli:cli" | |
| [tool.setuptools.packages.find] | |
| where = ["."] | |
| include = ["picarones*"] | |
| [tool.setuptools.package-data] | |
| picarones = [ | |
| "prompts/*.txt", | |
| "web/static/*.css", | |
| "web/static/*.js", | |
| "web/templates/*.j2", | |
| "web/templates/*.html", | |
| "report/templates/*.j2", | |
| "report/templates/*.html", | |
| "report/templates/*.css", | |
| "report/templates/*.js", | |
| "report/i18n/*.json", | |
| "measurements/narrative/templates/*.yaml", | |
| "data/*.yaml", | |
| "report/glossary/*.yaml", | |
| ] | |
| [tool.pytest.ini_options] | |
| testpaths = ["tests"] | |
| addopts = "-v --tb=short" | |
| # Sprint A1 (M-15) : aucun test individuel ne doit dépasser 5 minutes. | |
| # Mode "thread" car certains tests utilisent ProcessPoolExecutor qui est | |
| # incompatible avec le timeout en mode "signal" sur certaines plateformes. | |
| timeout = 300 | |
| timeout_method = "thread" | |
| # Marqueurs personnalisés. ``slow`` peut être désélectionné via | |
| # ``pytest -m "not slow"`` pour les boucles de dev. | |
| markers = [ | |
| "slow: tests longs (corpus de référence, intégration cloud) ; non bloquants en dev local", | |
| ] | |
| # ────────────────────────────────────────────────────────────────── | |
| # Sprint A1 (B-8) — seuil minimal de couverture appliqué en CI. | |
| # Le baseline est mesuré en début de sprint puis le plancher est posé | |
| # 2 points en dessous, pour laisser une marge de manœuvre aux PR | |
| # tout en interdisant une dégradation franche. | |
| # ────────────────────────────────────────────────────────────────── | |
| [tool.coverage.run] | |
| source = ["picarones"] | |
| omit = [ | |
| "picarones/report/vendor/*", # Chart.js minifié vendoré | |
| "picarones/report/templates/*", # templates Jinja2 + JS, pas du code Python | |
| "*/tests/*", | |
| ] | |
| parallel = true | |
| [tool.coverage.report] | |
| # Le seuil est appliqué via la flag CLI ``--cov-fail-under=N`` dans la CI | |
| # (cf. .github/workflows/ci.yml) plutôt qu'ici, pour permettre aux | |
| # développeurs de lancer ``pytest --cov`` localement sans échec sur les | |
| # fichiers qu'ils ne touchent pas. | |
| exclude_lines = [ | |
| "pragma: no cover", | |
| "raise NotImplementedError", | |
| "if TYPE_CHECKING:", | |
| "if __name__ == .__main__.:", | |
| ] | |
| # ────────────────────────────────────────────────────────────────── | |
| # Sprint A1 (M-4) — type-checking gradient. | |
| # | |
| # Stratégie : ``picarones.core`` est en mode ``strict`` car c'est la | |
| # couche la plus stable et l'API publique. Les autres cercles passent | |
| # en mode permissif (``ignore_missing_imports`` + pas de strict) — au | |
| # fur et à mesure des sprints suivants, on monte le niveau (Sprint A11 | |
| # resserre `picarones.measurements`). | |
| # ────────────────────────────────────────────────────────────────── | |
| [tool.mypy] | |
| python_version = "3.11" | |
| ignore_missing_imports = true | |
| warn_unused_configs = true | |
| warn_redundant_casts = true | |
| warn_unused_ignores = true | |
| no_implicit_optional = true | |
| # Les imports vers les autres cercles sont suivis silencieusement | |
| # pour éviter de propager les erreurs des cercles non encore typés. | |
| # Sprint A11 resserrera progressivement. | |
| follow_imports = "silent" | |
| [[tool.mypy.overrides]] | |
| module = "picarones.core.*" | |
| strict = true | |
| # A1 baseline : ces deux checks pré-existants génèrent ~70 % des erreurs | |
| # (annotations ``dict``/``tuple`` sans paramètres génériques, retours typés | |
| # ``Any``). Plutôt que de les fixer en bloc dans A1 et risquer une | |
| # régression, on les laisse explicitement désactivés et on les ré-active | |
| # en Sprint A11 (durcissement progressif du type-checking). | |
| disallow_any_generics = false | |
| warn_return_any = false | |
| # ────────────────────────────────────────────────────────────────── | |
| # Sprint A1 (B-7) — configuration bandit (scan sécurité statique). | |
| # | |
| # Politique : on refuse tout finding HIGH/CRITICAL en CI. Les MEDIUM | |
| # documentés ci-dessous comme "accepté" font l'objet d'un suivi explicite | |
| # (sprint cible mentionné). | |
| # | |
| # Exclusions documentées : | |
| # - B101 (assert_used) : pytest utilise systématiquement ``assert`` ; | |
| # - B105/B106 (hardcoded_password) : nos fixtures utilisent des chaînes | |
| # ``"password"`` dans des contextes purement de test ; | |
| # - B310 (urllib_urlopen) : tous nos appels ``urllib.urlopen`` ciblent | |
| # des endpoints HTTPS connus (Mistral, Google Vision, Azure DI, | |
| # Gallica, HF Hub, eScriptorium, Ollama). Un audit ligne par ligne | |
| # est tracé dans docs/audits/security-urllib-audit.md ; | |
| # - B608 (hardcoded_sql_expressions) : deux occurrences en | |
| # ``measurements/history.py:341`` et ``web/jobs.py:235`` ; la seconde | |
| # est un faux positif vérifié (audit institutional-readiness §6 F-1), | |
| # la première utilise une whitelist de colonnes documentée ; | |
| # - B615 (huggingface_unsafe_download) : à corriger en pinant la | |
| # ``revision`` dans extras/importers/huggingface.py — Sprint A5 ; | |
| # - B701 (jinja2_autoescape_false) : décision de design pré-existante | |
| # (cf. report/generator.py:606-611) ; les variables injectées sont | |
| # pré-échappées par les modules de rendu via ``html.escape``. | |
| # Refactor à effectuer dans le scope a11y (Sprint A6 ou A7) en | |
| # passant à ``select_autoescape`` + marquage ``|safe`` explicite des | |
| # blocs JSON/SVG. | |
| # ────────────────────────────────────────────────────────────────── | |
| [tool.bandit] | |
| exclude_dirs = ["tests", "picarones/report/vendor"] | |
| skips = ["B101", "B105", "B106", "B310", "B608", "B615", "B701"] | |
| [tool.ruff] | |
| # Configuration centralisée pour que `ruff check`, `make lint` et le job CI | |
| # produisent exactement les mêmes résultats sans flags en ligne de commande. | |
| line-length = 100 | |
| target-version = "py311" | |
| [tool.ruff.lint] | |
| # E/W = pycodestyle, F = pyflakes. On conserve les mêmes règles que le CI | |
| # d'origine (avant Sprint 22), qui excluait les lignes longues (E501) et les | |
| # imports non-top (E402, parfois utiles pour imports conditionnels). | |
| select = ["E", "W", "F"] | |
| ignore = ["E501", "E402"] | |