Spaces:
Running
docs: Phase 2 — vérité documentaire (compteurs, fantômes, legacy refs)
Browse filesSept corrections de drift documentation → code identifiées par
l'audit code-quality.
**2.1 — Sync compteurs en CI bloquant**
- Nouveau job CI ``sync-counters`` qui exécute
``scripts/gen_readme_tables.py --check`` : échoue si README.md /
CLAUDE.md divergent du code réel (script déjà présent depuis A13,
mais jamais câblé en CI ni Makefile — orphelin).
- Cibles Makefile ``sync-counters`` (régénère), ``sync-counters-check``
(vérifie), ``docs`` (``mkdocs build --strict``) et ``docs-serve``.
- CLAUDE.md : compteur 4 750 (réel) au lieu de 4 700, détail
cohérent 16 skipped + 8 deselected + 2 xfailed (auto-contradiction
L.119 vs L.305 résolue, la 2e mention renvoie maintenant à la 1re).
- CLAUDE.md : ``20 détecteurs`` au lieu de 18 ; ajout des 2 manquants
(``importer_fallback_triggered`` dans history.py et
``pricing_staleness_warning`` dans pareto.py). ``28 renderers HTML``
au lieu de 22.
- CLAUDE.md : note explicite sur le piège ``pytest tests/`` (uv tool)
→ ``python -m pytest tests/``.
**2.2 — Modules fantômes retirés de api-stable.md**
Le document affirme garantir l'existence des modules listés.
Quatre rubriques pointaient vers du code supprimé en v2.0 :
``picarones.pipeline.legacy_runner``,
``picarones.pipeline.legacy_pipeline_benchmark``,
``picarones.pipeline.legacy_pipeline_comparison``,
``picarones.evaluation.metrics.pipeline_spec_loader``.
Test ajouté : ``tests/docs/test_api_stable_modules_exist.py``
parse les rubriques ``### `picarones.X.Y``` et ``importlib.import_module``
chacune. Empêche la résurrection du drift.
**2.3 — README.md**
- Section ``Project layout`` : retrait du paragraphe « Legacy paths
still present as shims » (faux depuis v2.0). Remplacement par une
description honnête du retrait complet du legacy.
- Section ``Development`` : ``python -m mypy picarones/core/`` →
``python -m mypy picarones/domain/`` (strict) +
``python -m mypy picarones/`` (lax). ``pytest tests/`` →
``python -m pytest tests/``.
**2.5 — architecture.md**
Ligne 165 : ``22 renderers + 5 vues`` → ``28 renderers + 5 vues``.
Ligne 166 : ``18 détecteurs`` → ``20 détecteurs``.
**2.6 — pyproject.toml extra ``all``**
``all = ["picarones[web,hf,llm,dev]"]`` était trompeur (le commentaire
disait « tous les extras sauf OCR cloud » alors qu'il oubliait aussi
docs/stats/ner/pero/kraken/calamari). Élargi à l'intégralité :
``[dev,docs,web,stats,ner,hf,pero,kraken,calamari,llm,ocr-cloud]``.
Compteurs : 4 732 passed (post-Phase 1 : +1 test API-stable),
0 failed, ruff propre, sync-counters --check vert.
- .github/workflows/ci.yml +26 -0
- CLAUDE.md +23 -16
- Makefile +28 -0
- README.md +12 -8
- docs/explanation/architecture.md +2 -2
- docs/reference/api-stable.md +0 -49
- pyproject.toml +6 -2
- tests/docs/test_api_stable_modules_exist.py +68 -0
|
@@ -274,6 +274,32 @@ jobs:
|
|
| 274 |
- name: Run ruff
|
| 275 |
run: ruff check picarones/ tests/
|
| 276 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
# ──────────────────────────────────────────────────────────────────
|
| 278 |
# Job 5 : Type-checking — Sprint A1 (item M-4)
|
| 279 |
#
|
|
|
|
| 274 |
- name: Run ruff
|
| 275 |
run: ruff check picarones/ tests/
|
| 276 |
|
| 277 |
+
# ──────────────────────────────────────────────────────────────────
|
| 278 |
+
# Job 4-bis : Sync compteurs README/CLAUDE.md — Phase 2.1 audit
|
| 279 |
+
# code-quality (2026-05). Le script gen_readme_tables.py reflète
|
| 280 |
+
# le code réel dans la prose des docs (tableaux Engines/CLI/API +
|
| 281 |
+
# compteur de tests). En CI : exit 1 si la doc dérive.
|
| 282 |
+
# ──────────────────────────────────────────────────────────────────
|
| 283 |
+
sync-counters:
|
| 284 |
+
name: Doc counters sync
|
| 285 |
+
runs-on: ubuntu-latest
|
| 286 |
+
|
| 287 |
+
steps:
|
| 288 |
+
- name: Checkout
|
| 289 |
+
uses: actions/checkout@v4
|
| 290 |
+
|
| 291 |
+
- name: Set up Python
|
| 292 |
+
uses: actions/setup-python@v5
|
| 293 |
+
with:
|
| 294 |
+
python-version: "3.11"
|
| 295 |
+
cache: pip
|
| 296 |
+
|
| 297 |
+
- name: Install Picarones (web extras pour ouvrir l'app FastAPI)
|
| 298 |
+
run: pip install -e ".[dev,web]"
|
| 299 |
+
|
| 300 |
+
- name: Vérifier que README.md / CLAUDE.md reflètent le code
|
| 301 |
+
run: python scripts/gen_readme_tables.py --check
|
| 302 |
+
|
| 303 |
# ──────────────────────────────────────────────────────────────────
|
| 304 |
# Job 5 : Type-checking — Sprint A1 (item M-4)
|
| 305 |
#
|
|
@@ -95,9 +95,9 @@ picarones/
|
|
| 95 |
│ benchmark_runner (entry point CLI/web), partial_store
|
| 96 |
│
|
| 97 |
├── reports/ Couche 7 — rendu HTML / JSON / CSV
|
| 98 |
-
│ ├── html/ ReportGenerator +
|
| 99 |
│ ├── json/, csv/ exports tabulaires
|
| 100 |
-
│ ├── narrative/ moteur narratif (
|
| 101 |
│ ├── glossary/, i18n/ glossaire + i18n FR/EN
|
| 102 |
│ └── _helpers/ colors, render_helpers, assets
|
| 103 |
│
|
|
@@ -116,13 +116,19 @@ picarones/
|
|
| 116 |
|
| 117 |
## État des tests et bugs historiques
|
| 118 |
|
| 119 |
-
`pytest tests/` → **4750 passed,
|
| 120 |
-
(post-
|
| 121 |
-
contre vraie API/binaire) + `network`
|
| 122 |
-
opt-in en local via `pytest -m live`
|
| 123 |
-
|
| 124 |
-
`scripts/gen_readme_tables.py`
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
### Bugs documentés antérieurement — tous résolus
|
| 128 |
|
|
@@ -271,17 +277,18 @@ picarones/reports/narrative/
|
|
| 271 |
├── arbiter.py Tri par importance, non-redondance, anti-contradiction
|
| 272 |
├── renderer.py Rendu templates YAML par str.format_map (déterministe)
|
| 273 |
├── registry.py Registre par défaut des détecteurs
|
| 274 |
-
├── templates/{fr,en}.yaml
|
| 275 |
-
└── detectors/
|
| 276 |
├── ranking.py 5 (global_leader, statistical_tie, significant_gap,
|
| 277 |
│ speed_winner, median_mean_gap_warning)
|
| 278 |
-
├── pareto.py
|
|
|
|
| 279 |
├── stratum.py 3 (stratum_winner, stratum_collapse,
|
| 280 |
│ stratification_recommended)
|
| 281 |
├── quality.py 4 (error_profile_outlier, llm_hallucination_flag,
|
| 282 |
│ robustness_fragile, confidence_warning)
|
| 283 |
-
├── history.py
|
| 284 |
-
│ regression_in_history)
|
| 285 |
└── ensemble.py 1 (ensemble_opportunity)
|
| 286 |
```
|
| 287 |
|
|
@@ -302,8 +309,8 @@ détecte, arbitre, rend.
|
|
| 302 |
## Contexte développement
|
| 303 |
|
| 304 |
- **Environnement** : GitHub Codespaces, Python 3.11+
|
| 305 |
-
- **Tests** :
|
| 306 |
-
|
| 307 |
- **Manifeste architecture** : [`docs/explanation/architecture.md`](docs/explanation/architecture.md).
|
| 308 |
- **API publique stable** : [`docs/reference/api-stable.md`](docs/reference/api-stable.md).
|
| 309 |
|
|
|
|
| 95 |
│ benchmark_runner (entry point CLI/web), partial_store
|
| 96 |
│
|
| 97 |
├── reports/ Couche 7 — rendu HTML / JSON / CSV
|
| 98 |
+
│ ├── html/ ReportGenerator + 28 renderers + 5 vues + templates Jinja2
|
| 99 |
│ ├── json/, csv/ exports tabulaires
|
| 100 |
+
│ ├── narrative/ moteur narratif (20 détecteurs)
|
| 101 |
│ ├── glossary/, i18n/ glossaire + i18n FR/EN
|
| 102 |
│ └── _helpers/ colors, render_helpers, assets
|
| 103 |
│
|
|
|
|
| 116 |
|
| 117 |
## État des tests et bugs historiques
|
| 118 |
|
| 119 |
+
`pytest tests/` → **4750 passed, 16 skipped, 8 deselected, 2 xfailed, 0 failed**
|
| 120 |
+
(post-audit code-quality, mai 2026). Les deselected sont les markers
|
| 121 |
+
`live` (5 tests d'intégration contre vraie API/binaire) + `network`
|
| 122 |
+
(3 tests qui hit le réseau réel), opt-in en local via `pytest -m live`
|
| 123 |
+
ou `pytest -m network`. Le compteur ``passed`` est synchronisé
|
| 124 |
+
automatiquement par `scripts/gen_readme_tables.py` (CI : job
|
| 125 |
+
``sync-counters`` ; local : `make sync-counters-check`). Le détail
|
| 126 |
+
``skipped``/``xfailed`` peut dériver de ±2 entre éditions et n'est
|
| 127 |
+
pas verrouillé en CI.
|
| 128 |
+
|
| 129 |
+
NB : utiliser ``python -m pytest tests/`` plutôt que ``pytest tests/``
|
| 130 |
+
directement — l'installation via ``uv tool install pytest`` masque
|
| 131 |
+
les deps Picarones et produit ~160 collection errors trompeurs.
|
| 132 |
|
| 133 |
### Bugs documentés antérieurement — tous résolus
|
| 134 |
|
|
|
|
| 277 |
├── arbiter.py Tri par importance, non-redondance, anti-contradiction
|
| 278 |
├── renderer.py Rendu templates YAML par str.format_map (déterministe)
|
| 279 |
├── registry.py Registre par défaut des détecteurs
|
| 280 |
+
├── templates/{fr,en}.yaml 20 templates × 2 langues
|
| 281 |
+
└── detectors/ 20 détecteurs en 6 familles
|
| 282 |
├── ranking.py 5 (global_leader, statistical_tie, significant_gap,
|
| 283 |
│ speed_winner, median_mean_gap_warning)
|
| 284 |
+
├── pareto.py 3 (pareto_alternative, cost_outlier,
|
| 285 |
+
│ pricing_staleness_warning)
|
| 286 |
├── stratum.py 3 (stratum_winner, stratum_collapse,
|
| 287 |
│ stratification_recommended)
|
| 288 |
├── quality.py 4 (error_profile_outlier, llm_hallucination_flag,
|
| 289 |
│ robustness_fragile, confidence_warning)
|
| 290 |
+
├── history.py 4 (engine_off_baseline, engine_unstable,
|
| 291 |
+
│ regression_in_history, importer_fallback_triggered)
|
| 292 |
└── ensemble.py 1 (ensemble_opportunity)
|
| 293 |
```
|
| 294 |
|
|
|
|
| 309 |
## Contexte développement
|
| 310 |
|
| 311 |
- **Environnement** : GitHub Codespaces, Python 3.11+
|
| 312 |
+
- **Tests** : voir « État des tests et bugs historiques » plus haut
|
| 313 |
+
(compteur synchronisé par ``scripts/gen_readme_tables.py``).
|
| 314 |
- **Manifeste architecture** : [`docs/explanation/architecture.md`](docs/explanation/architecture.md).
|
| 315 |
- **API publique stable** : [`docs/reference/api-stable.md`](docs/reference/api-stable.md).
|
| 316 |
|
|
@@ -109,6 +109,34 @@ doc-check: ## Audit de cohérence README/SPECS/CHANGELOG (Sprint A2)
|
|
| 109 |
$(PYTHON) -m pytest tests/docs/ -q --tb=short --no-header; \
|
| 110 |
fi
|
| 111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
typecheck: ## Vérification de types avec mypy (si installé)
|
| 113 |
@$(VENV_BIN)/python -m mypy $(PACKAGE)/ --ignore-missing-imports --no-strict-optional 2>/dev/null \
|
| 114 |
|| echo "mypy non installé : pip install mypy"
|
|
|
|
| 109 |
$(PYTHON) -m pytest tests/docs/ -q --tb=short --no-header; \
|
| 110 |
fi
|
| 111 |
|
| 112 |
+
sync-counters: ## Régénère README/CLAUDE.md avec les compteurs réels (script gen_readme_tables.py)
|
| 113 |
+
@if [ -x $(VENV_BIN)/python ]; then \
|
| 114 |
+
$(VENV_BIN)/python scripts/gen_readme_tables.py; \
|
| 115 |
+
else \
|
| 116 |
+
$(PYTHON) scripts/gen_readme_tables.py; \
|
| 117 |
+
fi
|
| 118 |
+
|
| 119 |
+
sync-counters-check: ## CI : échoue si README/CLAUDE.md divergent du code (compteurs tests + tables)
|
| 120 |
+
@if [ -x $(VENV_BIN)/python ]; then \
|
| 121 |
+
$(VENV_BIN)/python scripts/gen_readme_tables.py --check; \
|
| 122 |
+
else \
|
| 123 |
+
$(PYTHON) scripts/gen_readme_tables.py --check; \
|
| 124 |
+
fi
|
| 125 |
+
|
| 126 |
+
docs: ## Construit le site mkdocs en mode strict (échoue sur les warnings)
|
| 127 |
+
@if [ -x $(VENV_BIN)/python ]; then \
|
| 128 |
+
$(VENV_BIN)/python -m mkdocs build --strict; \
|
| 129 |
+
else \
|
| 130 |
+
$(PYTHON) -m mkdocs build --strict; \
|
| 131 |
+
fi
|
| 132 |
+
|
| 133 |
+
docs-serve: ## Lance mkdocs en mode dev (http://localhost:8000)
|
| 134 |
+
@if [ -x $(VENV_BIN)/python ]; then \
|
| 135 |
+
$(VENV_BIN)/python -m mkdocs serve; \
|
| 136 |
+
else \
|
| 137 |
+
$(PYTHON) -m mkdocs serve; \
|
| 138 |
+
fi
|
| 139 |
+
|
| 140 |
typecheck: ## Vérification de types avec mypy (si installé)
|
| 141 |
@$(VENV_BIN)/python -m mypy $(PACKAGE)/ --ignore-missing-imports --no-strict-optional 2>/dev/null \
|
| 142 |
|| echo "mypy non installé : pip install mypy"
|
|
@@ -332,13 +332,16 @@ picarones/
|
|
| 332 |
└── interfaces/ Layer 8 — CLI Click, Web FastAPI
|
| 333 |
```
|
| 334 |
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
(
|
| 338 |
-
|
| 339 |
-
`
|
|
|
|
|
|
|
| 340 |
[`docs/explanation/architecture.md`](docs/explanation/architecture.md)
|
| 341 |
-
for the full manifesto
|
|
|
|
| 342 |
|
| 343 |
---
|
| 344 |
|
|
@@ -392,9 +395,10 @@ GitHub Actions: `.github/workflows/`
|
|
| 392 |
```bash
|
| 393 |
pip install -e ".[dev,web]"
|
| 394 |
pre-commit install
|
| 395 |
-
pytest tests/ -q
|
| 396 |
ruff check picarones/ tests/
|
| 397 |
-
python -m mypy picarones/
|
|
|
|
| 398 |
```
|
| 399 |
|
| 400 |
**Test suite**: ~4750 tests, ~3 min on a modern laptop. Coverage
|
|
|
|
| 332 |
└── interfaces/ Layer 8 — CLI Click, Web FastAPI
|
| 333 |
```
|
| 334 |
|
| 335 |
+
Strict 8-layer architecture: imports flow outer → inner. Enforced
|
| 336 |
+
by `tests/architecture/test_layer_dependencies.py`. The v2.0
|
| 337 |
+
release (May 2026) removed all legacy top-level packages (`core/`,
|
| 338 |
+
`measurements/`, `engines/`, `llm/`, `pipelines/`, `report/`,
|
| 339 |
+
`modules/`, `cli/`, `web/`, `extras/`) and the transitional
|
| 340 |
+
sub-packages (`adapters/legacy_engines/`, `adapters/legacy_pipelines/`,
|
| 341 |
+
`interfaces/{cli,web}/_legacy/`). See
|
| 342 |
[`docs/explanation/architecture.md`](docs/explanation/architecture.md)
|
| 343 |
+
for the full manifesto and migration history under
|
| 344 |
+
`docs/archives/migration/`.
|
| 345 |
|
| 346 |
---
|
| 347 |
|
|
|
|
| 395 |
```bash
|
| 396 |
pip install -e ".[dev,web]"
|
| 397 |
pre-commit install
|
| 398 |
+
python -m pytest tests/ -q # ``python -m`` requis si pytest est uv-installé
|
| 399 |
ruff check picarones/ tests/
|
| 400 |
+
python -m mypy picarones/domain/ # strict mode (Layer 1)
|
| 401 |
+
python -m mypy picarones/ # lax mode (full tree)
|
| 402 |
```
|
| 403 |
|
| 404 |
**Test suite**: ~4750 tests, ~3 min on a modern laptop. Coverage
|
|
@@ -162,8 +162,8 @@ Verrouillé par `tests/security/test_s1_zip_slip_attack.py`.
|
|
| 162 |
| `csv/render.py` | `CsvReportRenderer` — un CSV plat (`run_id, doc, pipeline, view, metric, value, status`) |
|
| 163 |
| `json/render.py` | `JsonReportRenderer` — manifest + documents en JSON déterministe |
|
| 164 |
| `html/render.py` | `HtmlReportRenderer` — rapport autonome (TextView, AltoView, SearchView) — minimaliste |
|
| 165 |
-
| `html/generator.py` | `ReportGenerator` — rapport interactif riche (
|
| 166 |
-
| `narrative/` | Moteur narratif (
|
| 167 |
| `glossary/`, `i18n/` | Glossaire + i18n FR/EN |
|
| 168 |
|
| 169 |
Le rendu est strict : pas de JS dynamique côté serveur, pas d'I/O
|
|
|
|
| 162 |
| `csv/render.py` | `CsvReportRenderer` — un CSV plat (`run_id, doc, pipeline, view, metric, value, status`) |
|
| 163 |
| `json/render.py` | `JsonReportRenderer` — manifest + documents en JSON déterministe |
|
| 164 |
| `html/render.py` | `HtmlReportRenderer` — rapport autonome (TextView, AltoView, SearchView) — minimaliste |
|
| 165 |
+
| `html/generator.py` | `ReportGenerator` — rapport interactif riche (28 renderers + 5 vues) consommé par CLI/web |
|
| 166 |
+
| `narrative/` | Moteur narratif (20 détecteurs) — synthèse factuelle déterministe |
|
| 167 |
| `glossary/`, `i18n/` | Glossaire + i18n FR/EN |
|
| 168 |
|
| 169 |
Le rendu est strict : pas de JS dynamique côté serveur, pas d'I/O
|
|
@@ -135,55 +135,6 @@ l'API mono-call historique de
|
|
| 135 |
s'appuyant en interne sur ``BenchmarkService`` (rewrite).
|
| 136 |
Prouvé numériquement équivalent en D.1.e.
|
| 137 |
|
| 138 |
-
### `picarones.pipeline.legacy_runner`
|
| 139 |
-
|
| 140 |
-
> Phase 7.B.2 (2026-05-07) — module relocalisé depuis
|
| 141 |
-
> ``picarones.evaluation.pipeline`` vers ``picarones.pipeline.legacy_runner``.
|
| 142 |
-
> La délégation au ``PipelineExecutor`` canonique impose à ce module
|
| 143 |
-
> d'importer la couche ``pipeline/`` — interdit à ``evaluation/``.
|
| 144 |
-
|
| 145 |
-
```python
|
| 146 |
-
class PipelineStep:
|
| 147 |
-
class PipelineSpec:
|
| 148 |
-
class StepResult:
|
| 149 |
-
class PipelineResult:
|
| 150 |
-
class PipelineRunner:
|
| 151 |
-
```
|
| 152 |
-
|
| 153 |
-
### `picarones.pipeline.legacy_pipeline_benchmark`
|
| 154 |
-
|
| 155 |
-
> Phase 7.B.2 — relocalisé depuis ``picarones.evaluation.pipeline_benchmark``
|
| 156 |
-
> (mêmes raisons que ``legacy_runner``).
|
| 157 |
-
|
| 158 |
-
```python
|
| 159 |
-
class StepAggregate:
|
| 160 |
-
class PipelineBenchmarkResult:
|
| 161 |
-
|
| 162 |
-
def default_initial_inputs(doc) -> dict
|
| 163 |
-
def run_pipeline_benchmark(spec, corpus, factory=...) -> PipelineBenchmarkResult
|
| 164 |
-
```
|
| 165 |
-
|
| 166 |
-
### `picarones.pipeline.legacy_pipeline_comparison`
|
| 167 |
-
|
| 168 |
-
> Phase 7.B.2 — relocalisé depuis ``picarones.evaluation.pipeline_comparison``.
|
| 169 |
-
|
| 170 |
-
```python
|
| 171 |
-
class PipelineComparisonResult:
|
| 172 |
-
|
| 173 |
-
def compare_pipelines(specs, corpus, factories=None) -> PipelineComparisonResult
|
| 174 |
-
```
|
| 175 |
-
|
| 176 |
-
### `picarones.evaluation.metrics.pipeline_spec_loader`
|
| 177 |
-
|
| 178 |
-
```python
|
| 179 |
-
class PipelineSpecLoadError(ValueError):
|
| 180 |
-
|
| 181 |
-
def load_pipeline_spec_from_yaml(path) -> PipelineSpec
|
| 182 |
-
def load_pipeline_spec_from_dict(data: dict) -> PipelineSpec
|
| 183 |
-
def load_comparison_specs_from_yaml(path) -> tuple[list[PipelineSpec], dict]
|
| 184 |
-
def load_comparison_specs_from_dict(data: dict) -> tuple[list[PipelineSpec], dict]
|
| 185 |
-
```
|
| 186 |
-
|
| 187 |
### `picarones.evaluation.metric_registry`
|
| 188 |
|
| 189 |
```python
|
|
|
|
| 135 |
s'appuyant en interne sur ``BenchmarkService`` (rewrite).
|
| 136 |
Prouvé numériquement équivalent en D.1.e.
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
### `picarones.evaluation.metric_registry`
|
| 139 |
|
| 140 |
```python
|
|
@@ -124,9 +124,13 @@ ocr-cloud = [
|
|
| 124 |
# distincts (``picarones-historical``, ``picarones-importers``) est
|
| 125 |
# documentée dans ``docs/developer/module-policy.md`` (Sprint 97) et
|
| 126 |
# n'a plus besoin d'être réservée par un extra vide.
|
| 127 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
all = [
|
| 129 |
-
"picarones[web,hf,llm,
|
| 130 |
]
|
| 131 |
|
| 132 |
[project.scripts]
|
|
|
|
| 124 |
# distincts (``picarones-historical``, ``picarones-importers``) est
|
| 125 |
# documentée dans ``docs/developer/module-policy.md`` (Sprint 97) et
|
| 126 |
# n'a plus besoin d'être réservée par un extra vide.
|
| 127 |
+
#
|
| 128 |
+
# Installation **vraiment complète** : tous les extras déclarés
|
| 129 |
+
# ci-dessus, OCR cloud et docs inclus. Le nom ``all`` ne doit pas
|
| 130 |
+
# tromper le contributeur — si un extra apparaît plus haut, il doit
|
| 131 |
+
# apparaître ici. Phase 2.6 de l'audit code-quality (2026-05).
|
| 132 |
all = [
|
| 133 |
+
"picarones[dev,docs,web,stats,ner,hf,pero,kraken,calamari,llm,ocr-cloud]",
|
| 134 |
]
|
| 135 |
|
| 136 |
[project.scripts]
|
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Phase 2.2 du plan d'audit — chaque module ``picarones.X.Y`` cité
|
| 2 |
+
dans ``docs/reference/api-stable.md`` comme rubrique de niveau 3
|
| 3 |
+
(``### `picarones....```) doit être réellement importable.
|
| 4 |
+
|
| 5 |
+
Le document revendique en son préambule :
|
| 6 |
+
|
| 7 |
+
> Garantie principale : **existence** — aucun nom listé ne
|
| 8 |
+
> disparaît entre ``1.x.0`` et ``2.0.0`` sans procédure de
|
| 9 |
+
> dépréciation.
|
| 10 |
+
|
| 11 |
+
L'audit code-quality de mai 2026 a trouvé que 4 modules cités
|
| 12 |
+
n'existaient plus :
|
| 13 |
+
|
| 14 |
+
- ``picarones.pipeline.legacy_runner``
|
| 15 |
+
- ``picarones.pipeline.legacy_pipeline_benchmark``
|
| 16 |
+
- ``picarones.pipeline.legacy_pipeline_comparison``
|
| 17 |
+
- ``picarones.evaluation.metrics.pipeline_spec_loader``
|
| 18 |
+
|
| 19 |
+
Tous supprimés au passage v2.0 (retrait du legacy). Ce test
|
| 20 |
+
empêche le drift de se reproduire.
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
from __future__ import annotations
|
| 24 |
+
|
| 25 |
+
import importlib
|
| 26 |
+
import re
|
| 27 |
+
from pathlib import Path
|
| 28 |
+
|
| 29 |
+
API_STABLE_MD = Path(__file__).resolve().parents[2] / "docs" / "reference" / "api-stable.md"
|
| 30 |
+
|
| 31 |
+
# Capture les rubriques de niveau 3 ``### `picarones.X.Y```.
|
| 32 |
+
# Le backtick fermant est obligatoire (évite de matcher du prose
|
| 33 |
+
# qui mentionne ``picarones.X`` sans intention de "rubrique API").
|
| 34 |
+
_RUBRIC_RE = re.compile(r"^###\s+`(picarones\.[\w\.]+)`", re.MULTILINE)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def _extract_modules() -> list[str]:
|
| 38 |
+
if not API_STABLE_MD.exists():
|
| 39 |
+
return []
|
| 40 |
+
return _RUBRIC_RE.findall(API_STABLE_MD.read_text(encoding="utf-8"))
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def test_every_api_stable_rubric_is_importable() -> None:
|
| 44 |
+
"""Chaque module cité comme ``### `picarones.X.Y``` doit s'importer
|
| 45 |
+
sans erreur — garantie d'existence pour les consommateurs externes
|
| 46 |
+
qui se fient à la documentation pour cibler une API stable.
|
| 47 |
+
"""
|
| 48 |
+
modules = _extract_modules()
|
| 49 |
+
assert modules, (
|
| 50 |
+
f"{API_STABLE_MD} ne contient aucune rubrique ``### `picarones.X``` — "
|
| 51 |
+
f"le test ne peut pas vérifier le contrat d'existence."
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
missing: list[tuple[str, str]] = []
|
| 55 |
+
for name in modules:
|
| 56 |
+
try:
|
| 57 |
+
importlib.import_module(name)
|
| 58 |
+
except ImportError as exc:
|
| 59 |
+
missing.append((name, str(exc)))
|
| 60 |
+
|
| 61 |
+
assert not missing, (
|
| 62 |
+
"api-stable.md référence des modules qui n'existent plus :\n"
|
| 63 |
+
+ "\n".join(f" - {name} : {err}" for name, err in missing)
|
| 64 |
+
+ "\n\nSoit recréer le module, soit retirer la rubrique de "
|
| 65 |
+
"``docs/reference/api-stable.md`` (et documenter la rupture dans "
|
| 66 |
+
"CHANGELOG.md). La garantie d'existence du document interdit "
|
| 67 |
+
"le drift silencieux."
|
| 68 |
+
)
|