Spaces:
Running
feat(migration): Lot B — core.{metric_registry,metric_hooks,metrics} → evaluation/
Browse filesSuite directe du Lot A. Tous les callers tests + doc utilisateur
des shims ``picarones.core.metric_registry``,
``picarones.core.metric_hooks`` et ``picarones.core.metrics`` ont
migré vers les canoniques
``picarones.evaluation.{metric_registry, metric_hooks,
metric_result}`` ; les shims sont **supprimés** dans le même
commit (suppression agressive, pas de shim qui survit à son
usage).
Imports tests migrés
--------------------
18 fichiers tests, ~45 statements d'import :
- ``from picarones.core.metric_registry import …``
→ ``from picarones.evaluation.metric_registry import …``
- ``from picarones.core.metric_hooks import …``
→ ``from picarones.evaluation.metric_hooks import …``
- ``from picarones.core.metrics import …``
→ ``from picarones.evaluation.metric_result import …``
- ``import picarones.core.metric_hooks as mh``
→ ``import picarones.evaluation.metric_hooks as mh``
Inclut les imports privés ``_METRIC_REGISTRY`` (test_sprint34),
``_CORPUS_AGGREGATORS``, ``_DOCUMENT_HOOKS``,
``_all_corpus_aggregator_names``, ``_all_document_hook_names``,
``_reset_for_tests`` (tests/core/test_metric_hooks.py) qui
existent tels quels dans les canoniques.
Doc utilisateur migrée
----------------------
- ``docs/reference/api-stable.md`` : sections
``picarones.core.metric_registry`` et
``picarones.core.metric_hooks`` réécrites en
``picarones.evaluation.metric_*``.
- ``docs/reference/normalization-profiles.md`` : 3 références
(chemins liens + un bloc d'imports) migrés.
Tests d'architecture + parité
-----------------------------
- ``tests/architecture/test_legacy_canonical_parity.py`` :
13 entrées (MetricSpec / register_metric /
compute_at_junction / select_metrics / get_metric /
all_metrics / register_document_metric /
register_corpus_aggregator / PROFILE_STANDARD /
PROFILE_FULL / PROFILE_MINIMAL / MetricsResult /
aggregate_metrics) supprimées en même temps que les shims.
La table ne tracke plus que ce qui existe sur disque (Lot A
+ Lot B retirent ainsi 7+13 = 20 entrées au total).
- ``tests/architecture/test_doc_paths.py`` :
``BROKEN_PATHS_BASELINE`` 77 → 80. Trois nouveaux chemins
cassés sur les shims supprimés : 2 dans ``CHANGELOG.md``
(intouchable) + 1 dans ``docs/migration/executor-equivalence.md``
(audit historique de la migration legacy → executor).
Le doc actif ``docs/reference/normalization-profiles.md`` a
été corrigé en place.
Tests consommateurs ajustés
---------------------------
- ``tests/core/test_public_api.py`` :
- Section 7 (« picarones.core.metric_registry — registre typé »)
pointe maintenant vers le canonique.
- Section 8 idem pour ``metric_hooks`` ; l'import alias
``from picarones.core import metric_hooks`` devient
``from picarones.evaluation import metric_hooks``.
- La liste de modules attendue dans ``api-stable.md`` est
ajustée.
- ``tests/core/test_metric_hooks.py`` : docstring d'en-tête
cite désormais ``picarones.evaluation.metric_hooks``.
Production / docstrings
-----------------------
Les docstrings actifs dans ``picarones/measurements/`` et
``picarones/evaluation/`` qui référençaient ``picarones.core.metric_*``
sont mis à jour vers les canoniques :
- ``picarones/measurements/{alto_metrics, builtin_hooks,
metrics, runner/aggregation, __init__}`` : 5 mentions
migrées.
- ``picarones/evaluation/registry/registry.py`` : la note
comparative « Différence avec l'existant
``picarones.core.metric_registry`` » devient
« Différence avec ``picarones.evaluation.metric_registry`` »
(l'autre registre n'est plus legacy).
- ``tests/evaluation/test_sprint_a14_s5_registry.py:236`` :
même mise à jour.
Les docstrings historiques en tête des canoniques
(``picarones/evaluation/metric_*.py:« Module relocalisé
depuis picarones.core.metric_* »``) sont volontairement
conservés comme trace de la migration.
``picarones/core/__init__.py`` : retrait des entrées
``metrics``, ``metric_registry``, ``metric_hooks`` de la
liste des modules ; pointeur explicite vers les canoniques
ajouté à la section « Modules retirés ».
Sync README + CLAUDE.md
-----------------------
``scripts/gen_readme_tables.py`` ré-exécuté : compteur de
tests global passe de 5110 → 5100 (suppression des 13
entrées parametrize de ``test_legacy_canonical_parity`` +
arithmétique des autres tests touchés). Toujours 0 failed
au-delà des 91 préexistants liés aux templates Jinja2.
Acceptance
----------
- ``pytest tests/architecture/`` : 108 passed.
- ``pytest tests/`` : seul le test
``test_readme_dual_lang::test_readme_tables_consistent_with_code``
était en échec après le ré-import — corrigé par
``gen_readme_tables.py`` ; aucune autre nouvelle régression
vs état Lot A (les 91 failed + 89 errors préexistants
sont identiques avant/après Lot B).
- ``ruff check picarones/ tests/`` : All checks passed.
Prochaine étape (Lot C) : migrer ``core.results`` →
``evaluation.benchmark_result``, ``core.corpus`` →
``evaluation.corpus``, ``core.pipeline`` →
``evaluation.pipeline`` (cf. SESSION_HANDOVER §4.D point 3).
https://claude.ai/code/session_011XQZNitg1rCgia8ZD1a2hP
- CLAUDE.md +3 -3
- README.md +1 -1
- docs/migration/SESSION_HANDOVER.md +16 -9
- docs/reference/api-stable.md +2 -2
- docs/reference/normalization-profiles.md +3 -3
- picarones/core/__init__.py +6 -6
- picarones/core/metric_hooks.py +0 -25
- picarones/core/metric_registry.py +0 -22
- picarones/core/metrics.py +0 -18
- picarones/evaluation/registry/registry.py +4 -3
- picarones/measurements/__init__.py +1 -1
- picarones/measurements/alto_metrics.py +1 -1
- picarones/measurements/builtin_hooks.py +1 -1
- picarones/measurements/metrics.py +2 -2
- picarones/measurements/runner/aggregation.py +1 -1
- tests/architecture/test_doc_paths.py +13 -1
- tests/architecture/test_legacy_canonical_parity.py +14 -40
- tests/core/test_metric_hooks.py +18 -18
- tests/core/test_public_api.py +11 -11
- tests/core/test_sprint34_metric_registry.py +5 -5
- tests/core/test_sprint_a14_s1_compact_optin.py +1 -1
- tests/core/test_sprint_a14_s1_metrics_error_returns_none.py +1 -1
- tests/evaluation/test_sprint_a14_s5_registry.py +2 -2
- tests/integration/test_alto_baseline.py +1 -1
- tests/integration/test_pipeline_ocr_to_alto.py +1 -1
- tests/measurements/test_sprint38_ner_metrics.py +1 -1
- tests/measurements/test_sprint52_readability.py +2 -2
- tests/measurements/test_sprint53_reading_order.py +1 -1
- tests/measurements/test_sprint55_unicode_blocks.py +1 -1
- tests/measurements/test_sprint56_abbreviations.py +1 -1
- tests/measurements/test_sprint57_mufi.py +1 -1
- tests/measurements/test_sprint58_early_modern.py +1 -1
- tests/measurements/test_sprint59_modern_archives.py +1 -1
- tests/measurements/test_sprint60_roman_numerals.py +1 -1
- tests/measurements/test_sprint84_searchability.py +2 -2
- tests/measurements/test_sprint85_numerical_sequences.py +2 -2
|
@@ -118,7 +118,7 @@ picarones/
|
|
| 118 |
|
| 119 |
## État des tests et bugs historiques
|
| 120 |
|
| 121 |
-
`pytest tests/` → **
|
| 122 |
(post-S59). Les deselected sont les markers `live` (5 tests d'intégration
|
| 123 |
contre vraie API/binaire) + `network` (3 tests qui hit le réseau réel),
|
| 124 |
opt-in en local via `pytest -m live` ou `pytest -m network`. Le
|
|
@@ -248,7 +248,7 @@ Résumé express :
|
|
| 248 |
|
| 249 |
1. `git branch --show-current` → `claude/repo-analysis-cukvm`.
|
| 250 |
2. `git status` → working tree clean.
|
| 251 |
-
3. `pytest tests/ -q --no-header --tb=line` →
|
| 252 |
4. `git log -1 --format=%B` → décrit la prochaine sub-phase.
|
| 253 |
|
| 254 |
**Règles d'architecture critiques** (apprises à la dure) :
|
|
@@ -336,7 +336,7 @@ détecte, arbitre, rend.
|
|
| 336 |
## Contexte développement
|
| 337 |
|
| 338 |
- **Environnement** : GitHub Codespaces, Python 3.11+
|
| 339 |
-
- **Tests** : `pytest tests/ -q` →
|
| 340 |
deselected, 0 failed (au moment de la pause de session).
|
| 341 |
- **Plan d'évolution actif** : [`docs/roadmap/evolution-2026.md`](docs/roadmap/evolution-2026.md).
|
| 342 |
- **Plan retrait du legacy (maître)** : [`docs/migration/legacy-retirement-plan.md`](docs/migration/legacy-retirement-plan.md).
|
|
|
|
| 118 |
|
| 119 |
## État des tests et bugs historiques
|
| 120 |
|
| 121 |
+
`pytest tests/` → **5100 passed, 12 skipped, 8 deselected, 0 failed**
|
| 122 |
(post-S59). Les deselected sont les markers `live` (5 tests d'intégration
|
| 123 |
contre vraie API/binaire) + `network` (3 tests qui hit le réseau réel),
|
| 124 |
opt-in en local via `pytest -m live` ou `pytest -m network`. Le
|
|
|
|
| 248 |
|
| 249 |
1. `git branch --show-current` → `claude/repo-analysis-cukvm`.
|
| 250 |
2. `git status` → working tree clean.
|
| 251 |
+
3. `pytest tests/ -q --no-header --tb=line` → 5100 passed.
|
| 252 |
4. `git log -1 --format=%B` → décrit la prochaine sub-phase.
|
| 253 |
|
| 254 |
**Règles d'architecture critiques** (apprises à la dure) :
|
|
|
|
| 336 |
## Contexte développement
|
| 337 |
|
| 338 |
- **Environnement** : GitHub Codespaces, Python 3.11+
|
| 339 |
+
- **Tests** : `pytest tests/ -q` → 5100 passed, 12 skipped, 24
|
| 340 |
deselected, 0 failed (au moment de la pause de session).
|
| 341 |
- **Plan d'évolution actif** : [`docs/roadmap/evolution-2026.md`](docs/roadmap/evolution-2026.md).
|
| 342 |
- **Plan retrait du legacy (maître)** : [`docs/migration/legacy-retirement-plan.md`](docs/migration/legacy-retirement-plan.md).
|
|
@@ -395,7 +395,7 @@ ruff check picarones/ tests/
|
|
| 395 |
python -m mypy picarones/core/
|
| 396 |
```
|
| 397 |
|
| 398 |
-
**Test suite**: ~
|
| 399 |
floor at 85% (currently ~87%). The `network` marker excludes tests
|
| 400 |
requiring live HTTP. A handful of tests depend on optional engines
|
| 401 |
(`pero-ocr`, `pytesseract`) and are skipped/fail gracefully when
|
|
|
|
| 395 |
python -m mypy picarones/core/
|
| 396 |
```
|
| 397 |
|
| 398 |
+
**Test suite**: ~5100 tests, ~3 min on a modern laptop. Coverage
|
| 399 |
floor at 85% (currently ~87%). The `network` marker excludes tests
|
| 400 |
requiring live HTTP. A handful of tests depend on optional engines
|
| 401 |
(`pero-ocr`, `pytesseract`) and are skipped/fail gracefully when
|
|
@@ -203,10 +203,10 @@ fiable.)
|
|
| 203 |
|
| 204 |
### 4.A Imports legacy dans les tests
|
| 205 |
|
| 206 |
-
**
|
| 207 |
paquets legacy (``core``, ``measurements``, ``engines``,
|
| 208 |
-
``llm``, ``pipelines``, ``report``, ``modules``) —
|
| 209 |
-
|
| 210 |
|
| 211 |
Top chemins consommés :
|
| 212 |
|
|
@@ -222,11 +222,12 @@ Top chemins consommés :
|
|
| 222 |
au lieu de pointer vers le canonique. Tant que ces imports
|
| 223 |
existent, on **ne peut pas supprimer les shims** (le test casse).
|
| 224 |
|
| 225 |
-
**Stratégie** : sed batch par chemin
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
``core.
|
| 229 |
-
|
|
|
|
| 230 |
|
| 231 |
### 4.B Imports legacy en production (hors shims eux-mêmes)
|
| 232 |
|
|
@@ -260,10 +261,16 @@ L'ordre recommandé, par lots de symboles cohérents :
|
|
| 260 |
supprimés ; doc utilisateur (tutorials/, developer/,
|
| 261 |
reference/api-stable.md, explanation/narrative-engine.en.md)
|
| 262 |
pointe maintenant vers les canoniques.
|
| 263 |
-
2. **Lot B — evaluation/metric_*** (~
|
|
|
|
| 264 |
- ``core.metric_registry.*`` → ``evaluation.metric_registry.*``
|
| 265 |
- ``core.metric_hooks.*`` → ``evaluation.metric_hooks.*``
|
| 266 |
- ``core.metrics.*`` → ``evaluation.metric_result.*``
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
3. **Lot C — evaluation/{benchmark_result, corpus, pipeline}** :
|
| 268 |
- ``core.results.*`` → ``evaluation.benchmark_result.*``
|
| 269 |
- ``core.corpus.*`` → ``evaluation.corpus.*``
|
|
|
|
| 203 |
|
| 204 |
### 4.A Imports legacy dans les tests
|
| 205 |
|
| 206 |
+
**101 fichiers** avec **526 statements** d'import depuis les
|
| 207 |
paquets legacy (``core``, ``measurements``, ``engines``,
|
| 208 |
+
``llm``, ``pipelines``, ``report``, ``modules``) — Lots A et B
|
| 209 |
+
terminés (cf. 4.D ci-dessous).
|
| 210 |
|
| 211 |
Top chemins consommés :
|
| 212 |
|
|
|
|
| 222 |
au lieu de pointer vers le canonique. Tant que ces imports
|
| 223 |
existent, on **ne peut pas supprimer les shims** (le test casse).
|
| 224 |
|
| 225 |
+
**Stratégie** : sed batch par chemin, valider les tests,
|
| 226 |
+
commit, avancer. Shims supprimés dans les Lots A
|
| 227 |
+
(``core.modules`` + ``core.facts``) et B
|
| 228 |
+
(``core.metric_registry`` + ``core.metric_hooks`` +
|
| 229 |
+
``core.metrics``) sur la branche
|
| 230 |
+
``claude/migrate-core-to-domain-8ubIT``.
|
| 231 |
|
| 232 |
### 4.B Imports legacy en production (hors shims eux-mêmes)
|
| 233 |
|
|
|
|
| 261 |
supprimés ; doc utilisateur (tutorials/, developer/,
|
| 262 |
reference/api-stable.md, explanation/narrative-engine.en.md)
|
| 263 |
pointe maintenant vers les canoniques.
|
| 264 |
+
2. ✅ **Lot B — evaluation/metric_*** (~45 imports migrés, shims
|
| 265 |
+
supprimés) :
|
| 266 |
- ``core.metric_registry.*`` → ``evaluation.metric_registry.*``
|
| 267 |
- ``core.metric_hooks.*`` → ``evaluation.metric_hooks.*``
|
| 268 |
- ``core.metrics.*`` → ``evaluation.metric_result.*``
|
| 269 |
+
- Shims ``picarones.core.metric_registry`` +
|
| 270 |
+
``picarones.core.metric_hooks`` + ``picarones.core.metrics``
|
| 271 |
+
supprimés ; ``docs/reference/normalization-profiles.md`` et
|
| 272 |
+
``docs/reference/api-stable.md`` migrés vers les chemins
|
| 273 |
+
canoniques.
|
| 274 |
3. **Lot C — evaluation/{benchmark_result, corpus, pipeline}** :
|
| 275 |
- ``core.results.*`` → ``evaluation.benchmark_result.*``
|
| 276 |
- ``core.corpus.*`` → ``evaluation.corpus.*``
|
|
@@ -158,7 +158,7 @@ def load_comparison_specs_from_yaml(path) -> tuple[list[PipelineSpec], dict]
|
|
| 158 |
def load_comparison_specs_from_dict(data: dict) -> tuple[list[PipelineSpec], dict]
|
| 159 |
```
|
| 160 |
|
| 161 |
-
### `picarones.
|
| 162 |
|
| 163 |
```python
|
| 164 |
class MetricSpec: # frozen dataclass : name, func, input_types, ...
|
|
@@ -170,7 +170,7 @@ def select_metrics(input_types) -> list[MetricSpec]
|
|
| 170 |
def compute_at_junction(reference, hypothesis, input_types, *, skip_on_error=True) -> dict
|
| 171 |
```
|
| 172 |
|
| 173 |
-
### `picarones.
|
| 174 |
|
| 175 |
```python
|
| 176 |
# Profils — constantes
|
|
|
|
| 158 |
def load_comparison_specs_from_dict(data: dict) -> tuple[list[PipelineSpec], dict]
|
| 159 |
```
|
| 160 |
|
| 161 |
+
### `picarones.evaluation.metric_registry`
|
| 162 |
|
| 163 |
```python
|
| 164 |
class MetricSpec: # frozen dataclass : name, func, input_types, ...
|
|
|
|
| 170 |
def compute_at_junction(reference, hypothesis, input_types, *, skip_on_error=True) -> dict
|
| 171 |
```
|
| 172 |
|
| 173 |
+
### `picarones.evaluation.metric_hooks`
|
| 174 |
|
| 175 |
```python
|
| 176 |
# Profils — constantes
|
|
@@ -4,7 +4,7 @@ Picarones expose **7 profils de calcul** qui modulent les métriques
|
|
| 4 |
calculées par le runner selon le use case. Chaque profil active un
|
| 5 |
sous-ensemble des **12 hooks document-level** et **12 agrégateurs
|
| 6 |
corpus-level** du registre central
|
| 7 |
-
([`picarones/
|
| 8 |
|
| 9 |
## Synoptique
|
| 10 |
|
|
@@ -131,7 +131,7 @@ Voir [`docs/explanation/narrative-engine.md`](developer/narrative-engine.md)
|
|
| 131 |
pour le détail. Pattern de base :
|
| 132 |
|
| 133 |
```python
|
| 134 |
-
from picarones.
|
| 135 |
register_document_metric, PROFILE_DIAGNOSTICS, PROFILE_FULL,
|
| 136 |
)
|
| 137 |
|
|
@@ -148,7 +148,7 @@ def my_hook(*, ground_truth, hypothesis, image_path, corpus_lang, ocr_result):
|
|
| 148 |
|
| 149 |
## Code source
|
| 150 |
|
| 151 |
-
- [`picarones/
|
| 152 |
— registre, profils, `run_document_hooks()`, `run_corpus_aggregators()`.
|
| 153 |
- [`picarones/measurements/builtin_hooks.py`](../picarones/measurements/builtin_hooks.py)
|
| 154 |
— les 12 hooks doc + 12 agrégateurs natifs Picarones.
|
|
|
|
| 4 |
calculées par le runner selon le use case. Chaque profil active un
|
| 5 |
sous-ensemble des **12 hooks document-level** et **12 agrégateurs
|
| 6 |
corpus-level** du registre central
|
| 7 |
+
([`picarones/evaluation/metric_hooks.py`](../picarones/evaluation/metric_hooks.py)).
|
| 8 |
|
| 9 |
## Synoptique
|
| 10 |
|
|
|
|
| 131 |
pour le détail. Pattern de base :
|
| 132 |
|
| 133 |
```python
|
| 134 |
+
from picarones.evaluation.metric_hooks import (
|
| 135 |
register_document_metric, PROFILE_DIAGNOSTICS, PROFILE_FULL,
|
| 136 |
)
|
| 137 |
|
|
|
|
| 148 |
|
| 149 |
## Code source
|
| 150 |
|
| 151 |
+
- [`picarones/evaluation/metric_hooks.py`](../picarones/evaluation/metric_hooks.py)
|
| 152 |
— registre, profils, `run_document_hooks()`, `run_corpus_aggregators()`.
|
| 153 |
- [`picarones/measurements/builtin_hooks.py`](../picarones/measurements/builtin_hooks.py)
|
| 154 |
— les 12 hooks doc + 12 agrégateurs natifs Picarones.
|
|
@@ -13,15 +13,15 @@ Modules
|
|
| 13 |
-------
|
| 14 |
- :mod:`corpus` Document, Corpus, GTLevel + payloads typés
|
| 15 |
- :mod:`results` DocumentResult, EngineReport, BenchmarkResult
|
| 16 |
-
- :mod:`metrics` MetricsResult (dataclass), aggregate_metrics
|
| 17 |
-
- :mod:`metric_registry` MetricSpec, register_metric, compute_at_junction
|
| 18 |
-
- :mod:`metric_hooks` register_document_metric, register_corpus_aggregator
|
| 19 |
- :mod:`pipeline` PipelineRunner, PipelineSpec, PipelineStep
|
| 20 |
|
| 21 |
-
Modules retirés (
|
| 22 |
|
| 23 |
-
- ``modules``
|
| 24 |
-
- ``facts``
|
|
|
|
|
|
|
|
|
|
| 25 |
|
| 26 |
Voir :doc:`docs/explanation/architecture.md` pour le manifeste complet et
|
| 27 |
:doc:`docs/reference/api-stable.md` pour le contrat de stabilité de chaque
|
|
|
|
| 13 |
-------
|
| 14 |
- :mod:`corpus` Document, Corpus, GTLevel + payloads typés
|
| 15 |
- :mod:`results` DocumentResult, EngineReport, BenchmarkResult
|
|
|
|
|
|
|
|
|
|
| 16 |
- :mod:`pipeline` PipelineRunner, PipelineSpec, PipelineStep
|
| 17 |
|
| 18 |
+
Modules retirés (Phase 4-bis et suivantes du retrait du legacy) :
|
| 19 |
|
| 20 |
+
- ``modules`` → ``picarones.domain.{artifacts, module_protocol}`` (Lot A).
|
| 21 |
+
- ``facts`` → ``picarones.domain.facts`` (Lot A).
|
| 22 |
+
- ``metrics`` → ``picarones.evaluation.metric_result`` (Lot B).
|
| 23 |
+
- ``metric_registry`` → ``picarones.evaluation.metric_registry`` (Lot B).
|
| 24 |
+
- ``metric_hooks`` → ``picarones.evaluation.metric_hooks`` (Lot B).
|
| 25 |
|
| 26 |
Voir :doc:`docs/explanation/architecture.md` pour le manifeste complet et
|
| 27 |
:doc:`docs/reference/api-stable.md` pour le contrat de stabilité de chaque
|
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
"""``picarones.core.metric_hooks`` — shim re-export (déprécié, suppression 2.0).
|
| 2 |
-
|
| 3 |
-
Canonique : :mod:`picarones.evaluation.metric_hooks`. Phase 4-ter
|
| 4 |
-
du retrait du legacy.
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
from __future__ import annotations
|
| 8 |
-
|
| 9 |
-
import warnings
|
| 10 |
-
|
| 11 |
-
from picarones.evaluation.metric_hooks import * # noqa: F401, F403
|
| 12 |
-
from picarones.evaluation.metric_hooks import ( # noqa: F401
|
| 13 |
-
_CORPUS_AGGREGATORS,
|
| 14 |
-
_DOCUMENT_HOOKS,
|
| 15 |
-
_all_corpus_aggregator_names,
|
| 16 |
-
_all_document_hook_names,
|
| 17 |
-
_reset_for_tests,
|
| 18 |
-
)
|
| 19 |
-
|
| 20 |
-
warnings.warn(
|
| 21 |
-
"picarones.core.metric_hooks is deprecated and will be removed in 2.0. "
|
| 22 |
-
"Import from picarones.evaluation.metric_hooks instead.",
|
| 23 |
-
DeprecationWarning,
|
| 24 |
-
stacklevel=2,
|
| 25 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,22 +0,0 @@
|
|
| 1 |
-
"""``picarones.core.metric_registry`` — shim re-export (déprécié, suppression 2.0).
|
| 2 |
-
|
| 3 |
-
Canonique : :mod:`picarones.evaluation.metric_registry`. Phase 4-ter
|
| 4 |
-
du retrait du legacy.
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
from __future__ import annotations
|
| 8 |
-
|
| 9 |
-
import warnings
|
| 10 |
-
|
| 11 |
-
from picarones.evaluation.metric_registry import * # noqa: F401, F403
|
| 12 |
-
from picarones.evaluation.metric_registry import ( # noqa: F401
|
| 13 |
-
_METRIC_REGISTRY,
|
| 14 |
-
_reset_registry_for_tests,
|
| 15 |
-
)
|
| 16 |
-
|
| 17 |
-
warnings.warn(
|
| 18 |
-
"picarones.core.metric_registry is deprecated and will be removed in 2.0. "
|
| 19 |
-
"Import from picarones.evaluation.metric_registry instead.",
|
| 20 |
-
DeprecationWarning,
|
| 21 |
-
stacklevel=2,
|
| 22 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1,18 +0,0 @@
|
|
| 1 |
-
"""``picarones.core.metrics`` — shim re-export (déprécié, suppression 2.0).
|
| 2 |
-
|
| 3 |
-
Canonique : :mod:`picarones.evaluation.metric_result`. Phase 4-ter
|
| 4 |
-
du retrait du legacy.
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
from __future__ import annotations
|
| 8 |
-
|
| 9 |
-
import warnings
|
| 10 |
-
|
| 11 |
-
from picarones.evaluation.metric_result import * # noqa: F401, F403
|
| 12 |
-
|
| 13 |
-
warnings.warn(
|
| 14 |
-
"picarones.core.metrics is deprecated and will be removed in 2.0. "
|
| 15 |
-
"Import from picarones.evaluation.metric_result instead.",
|
| 16 |
-
DeprecationWarning,
|
| 17 |
-
stacklevel=2,
|
| 18 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -6,9 +6,10 @@ de l'application (cf. ``picarones/app/services/registry_service.py``
|
|
| 6 |
au S20) — pas de singleton global, pas de side-effect d'import,
|
| 7 |
pas de décorateur magique.
|
| 8 |
|
| 9 |
-
Différence avec
|
| 10 |
-
--------------------------------------------------------
|
| 11 |
-
L'
|
|
|
|
| 12 |
``_METRIC_REGISTRY`` rempli par un décorateur ``@register_metric``
|
| 13 |
appliqué au top-level d'autres modules. Conséquence : un
|
| 14 |
``import picarones`` charge ~50 sous-modules pour amorcer le
|
|
|
|
| 6 |
au S20) — pas de singleton global, pas de side-effect d'import,
|
| 7 |
pas de décorateur magique.
|
| 8 |
|
| 9 |
+
Différence avec ``picarones.evaluation.metric_registry``
|
| 10 |
+
--------------------------------------------------------
|
| 11 |
+
L'autre registre (relocalisé depuis ``picarones.core.metric_registry``
|
| 12 |
+
en Phase 4-ter) utilise un dict module-level
|
| 13 |
``_METRIC_REGISTRY`` rempli par un décorateur ``@register_metric``
|
| 14 |
appliqué au top-level d'autres modules. Conséquence : un
|
| 15 |
``import picarones`` charge ~50 sous-modules pour amorcer le
|
|
@@ -125,7 +125,7 @@ la règle de dépendance des 3 cercles.
|
|
| 125 |
# qui violait la règle.
|
| 126 |
#
|
| 127 |
# Tout consommateur qui veut utiliser ``compute_at_junction``
|
| 128 |
-
# (``picarones.
|
| 129 |
# ``picarones.measurements`` au moins une fois pour que les décorateurs
|
| 130 |
# ``@register_metric`` aient été exécutés. C'est le cas par défaut dans
|
| 131 |
# le pipeline standard ; les notebooks isolés peuvent ajouter
|
|
|
|
| 125 |
# qui violait la règle.
|
| 126 |
#
|
| 127 |
# Tout consommateur qui veut utiliser ``compute_at_junction``
|
| 128 |
+
# (``picarones.evaluation.metric_registry``) doit avoir importé
|
| 129 |
# ``picarones.measurements`` au moins une fois pour que les décorateurs
|
| 130 |
# ``@register_metric`` aient été exécutés. C'est le cas par défaut dans
|
| 131 |
# le pipeline standard ; les notebooks isolés peuvent ajouter
|
|
@@ -41,7 +41,7 @@ Cas typique d'usage
|
|
| 41 |
Un VLM produit un ALTO via un reconstructeur (par exemple
|
| 42 |
:class:`picarones.modules.TextToAltoMonoRegion`). La GT
|
| 43 |
:class:`picarones.core.corpus.AltoGT` du document est confrontée à la
|
| 44 |
-
sortie via :func:`picarones.
|
| 45 |
qui sélectionne automatiquement les métriques ``(ALTO, ALTO)``
|
| 46 |
ci-dessous.
|
| 47 |
"""
|
|
|
|
| 41 |
Un VLM produit un ALTO via un reconstructeur (par exemple
|
| 42 |
:class:`picarones.modules.TextToAltoMonoRegion`). La GT
|
| 43 |
:class:`picarones.core.corpus.AltoGT` du document est confrontée à la
|
| 44 |
+
sortie via :func:`picarones.evaluation.metric_registry.compute_at_junction`,
|
| 45 |
qui sélectionne automatiquement les métriques ``(ALTO, ALTO)``
|
| 46 |
ci-dessous.
|
| 47 |
"""
|
|
@@ -17,7 +17,7 @@ CER/WER comptent). Les profils ``economics`` et ``pipeline`` sont
|
|
| 17 |
réservés pour des hooks futurs.
|
| 18 |
|
| 19 |
L'import de ce module **suffit** à peupler les registres :
|
| 20 |
-
:mod:`picarones.
|
| 21 |
décorateurs ; le runner ne dépend que d'une seule fonction —
|
| 22 |
``select_document_hooks(profile)`` — pour découvrir les hooks actifs.
|
| 23 |
|
|
|
|
| 17 |
réservés pour des hooks futurs.
|
| 18 |
|
| 19 |
L'import de ce module **suffit** à peupler les registres :
|
| 20 |
+
:mod:`picarones.evaluation.metric_hooks` se contente d'exposer les
|
| 21 |
décorateurs ; le runner ne dépend que d'une seule fonction —
|
| 22 |
``select_document_hooks(profile)`` — pour découvrir les hooks actifs.
|
| 23 |
|
|
@@ -15,8 +15,8 @@ Métriques implémentées
|
|
| 15 |
Modèle de données
|
| 16 |
-----------------
|
| 17 |
``MetricsResult`` (dataclass pure) et ``aggregate_metrics`` (stats
|
| 18 |
-
moyenne/médiane via ``statistics`` stdlib) vivent en
|
| 19 |
-
:mod:`picarones.
|
| 20 |
commodité — un module qui consomme déjà ``compute_metrics`` n'a
|
| 21 |
qu'à en faire ``from picarones.measurements.metrics import …``.
|
| 22 |
"""
|
|
|
|
| 15 |
Modèle de données
|
| 16 |
-----------------
|
| 17 |
``MetricsResult`` (dataclass pure) et ``aggregate_metrics`` (stats
|
| 18 |
+
moyenne/médiane via ``statistics`` stdlib) vivent en couche 3 dans
|
| 19 |
+
:mod:`picarones.evaluation.metric_result`. Ils sont ré-exportés ici pour la
|
| 20 |
commodité — un module qui consomme déjà ``compute_metrics`` n'a
|
| 21 |
qu'à en faire ``from picarones.measurements.metrics import …``.
|
| 22 |
"""
|
|
@@ -4,7 +4,7 @@ Chantier 2 (post-Sprint 97) : la logique d'agrégation par-engine de
|
|
| 4 |
toutes les métriques (confusion, taxonomy, structure, image_quality,
|
| 5 |
line_metrics, hallucination, calibration, char_scores) vit désormais
|
| 6 |
dans :mod:`picarones.measurements.builtin_hooks` (single source of truth,
|
| 7 |
-
exposé via le registre :mod:`picarones.
|
| 8 |
|
| 9 |
Les noms ci-dessous restent disponibles depuis
|
| 10 |
``picarones.measurements.runner`` pour la rétrocompat des tests
|
|
|
|
| 4 |
toutes les métriques (confusion, taxonomy, structure, image_quality,
|
| 5 |
line_metrics, hallucination, calibration, char_scores) vit désormais
|
| 6 |
dans :mod:`picarones.measurements.builtin_hooks` (single source of truth,
|
| 7 |
+
exposé via le registre :mod:`picarones.evaluation.metric_hooks`).
|
| 8 |
|
| 9 |
Les noms ci-dessous restent disponibles depuis
|
| 10 |
``picarones.measurements.runner`` pour la rétrocompat des tests
|
|
@@ -51,12 +51,24 @@ REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
| 51 |
#: ``CHANGELOG.md`` (journal versionné) et
|
| 52 |
#: ``docs/roadmap/evolution-2026.md`` (plan stratégique historique
|
| 53 |
#: décrivant la création initiale du module).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
#:
|
| 55 |
#: Les chemins cassés restants sont **TOUS** dans :
|
| 56 |
#: - ``CHANGELOG.md`` : journal historique versionné, intouchable.
|
| 57 |
#: - ``docs/audits/*.md`` : audits historiques, intouchables.
|
| 58 |
#: - ``docs/roadmap/evolution-2026.md`` : plan stratégique historique.
|
| 59 |
-
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
#: Patrons de fichiers de documentation à scanner.
|
| 62 |
DOC_GLOBS: tuple[str, ...] = (
|
|
|
|
| 51 |
#: ``CHANGELOG.md`` (journal versionné) et
|
| 52 |
#: ``docs/roadmap/evolution-2026.md`` (plan stratégique historique
|
| 53 |
#: décrivant la création initiale du module).
|
| 54 |
+
#: - 80 (sprint « Lot B — core.metric_* → evaluation », 2026-05-07) :
|
| 55 |
+
#: suppression des shims ``picarones/core/metric_registry.py``,
|
| 56 |
+
#: ``picarones/core/metric_hooks.py`` et
|
| 57 |
+
#: ``picarones/core/metrics.py``. Trois nouvelles références
|
| 58 |
+
#: héritées : deux dans ``CHANGELOG.md`` (intouchable) + une
|
| 59 |
+
#: dans ``docs/migration/executor-equivalence.md`` (audit
|
| 60 |
+
#: historique de la migration legacy → executor). Le doc actif
|
| 61 |
+
#: ``docs/reference/normalization-profiles.md`` a été corrigé
|
| 62 |
+
#: en place vers ``picarones/evaluation/metric_hooks.py``.
|
| 63 |
#:
|
| 64 |
#: Les chemins cassés restants sont **TOUS** dans :
|
| 65 |
#: - ``CHANGELOG.md`` : journal historique versionné, intouchable.
|
| 66 |
#: - ``docs/audits/*.md`` : audits historiques, intouchables.
|
| 67 |
#: - ``docs/roadmap/evolution-2026.md`` : plan stratégique historique.
|
| 68 |
+
#: - ``docs/migration/executor-equivalence.md`` : audit historique
|
| 69 |
+
#: d'équivalence executor (cite des chemins legacy à des fins
|
| 70 |
+
#: de comparaison).
|
| 71 |
+
BROKEN_PATHS_BASELINE = 80
|
| 72 |
|
| 73 |
#: Patrons de fichiers de documentation à scanner.
|
| 74 |
DOC_GLOBS: tuple[str, ...] = (
|
|
@@ -121,47 +121,21 @@ LEGACY_PARITY: dict[str, ParityEntry] = {
|
|
| 121 |
# retirées en même temps que les shims pour garder la table
|
| 122 |
# alignée avec l'arbre legacy réellement présent sur disque.
|
| 123 |
# ──────────────────────────────────────────────────────────
|
| 124 |
-
# Phase 4-ter — metric_registry, metric_hooks, metrics
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
# ──────────────────────────────────────────────────────────
|
| 126 |
-
"picarones.core.metric_registry.MetricSpec": {
|
| 127 |
-
"canonical": "picarones.evaluation.metric_registry.MetricSpec",
|
| 128 |
-
},
|
| 129 |
-
"picarones.core.metric_registry.register_metric": {
|
| 130 |
-
"canonical": "picarones.evaluation.metric_registry.register_metric",
|
| 131 |
-
},
|
| 132 |
-
"picarones.core.metric_registry.compute_at_junction": {
|
| 133 |
-
"canonical": "picarones.evaluation.metric_registry.compute_at_junction",
|
| 134 |
-
},
|
| 135 |
-
"picarones.core.metric_registry.select_metrics": {
|
| 136 |
-
"canonical": "picarones.evaluation.metric_registry.select_metrics",
|
| 137 |
-
},
|
| 138 |
-
"picarones.core.metric_registry.get_metric": {
|
| 139 |
-
"canonical": "picarones.evaluation.metric_registry.get_metric",
|
| 140 |
-
},
|
| 141 |
-
"picarones.core.metric_registry.all_metrics": {
|
| 142 |
-
"canonical": "picarones.evaluation.metric_registry.all_metrics",
|
| 143 |
-
},
|
| 144 |
-
"picarones.core.metric_hooks.register_document_metric": {
|
| 145 |
-
"canonical": "picarones.evaluation.metric_hooks.register_document_metric",
|
| 146 |
-
},
|
| 147 |
-
"picarones.core.metric_hooks.register_corpus_aggregator": {
|
| 148 |
-
"canonical": "picarones.evaluation.metric_hooks.register_corpus_aggregator",
|
| 149 |
-
},
|
| 150 |
-
"picarones.core.metric_hooks.PROFILE_STANDARD": {
|
| 151 |
-
"canonical": "picarones.evaluation.metric_hooks.PROFILE_STANDARD",
|
| 152 |
-
},
|
| 153 |
-
"picarones.core.metric_hooks.PROFILE_FULL": {
|
| 154 |
-
"canonical": "picarones.evaluation.metric_hooks.PROFILE_FULL",
|
| 155 |
-
},
|
| 156 |
-
"picarones.core.metric_hooks.PROFILE_MINIMAL": {
|
| 157 |
-
"canonical": "picarones.evaluation.metric_hooks.PROFILE_MINIMAL",
|
| 158 |
-
},
|
| 159 |
-
"picarones.core.metrics.MetricsResult": {
|
| 160 |
-
"canonical": "picarones.evaluation.metric_result.MetricsResult",
|
| 161 |
-
},
|
| 162 |
-
"picarones.core.metrics.aggregate_metrics": {
|
| 163 |
-
"canonical": "picarones.evaluation.metric_result.aggregate_metrics",
|
| 164 |
-
},
|
| 165 |
"picarones.core.results.BenchmarkResult": {
|
| 166 |
"canonical": "picarones.evaluation.benchmark_result.BenchmarkResult",
|
| 167 |
},
|
|
|
|
| 121 |
# retirées en même temps que les shims pour garder la table
|
| 122 |
# alignée avec l'arbre legacy réellement présent sur disque.
|
| 123 |
# ──────────────────────────────────────────────────────────
|
| 124 |
+
# Phase 4-ter — metric_registry, metric_hooks, metrics
|
| 125 |
+
# ──────────────────────────────────────────────────────────
|
| 126 |
+
# ``core.metric_registry``, ``core.metric_hooks`` et
|
| 127 |
+
# ``core.metrics`` ont été supprimés (Lot B de la migration
|
| 128 |
+
# core → evaluation). Les symboles publics
|
| 129 |
+
# (MetricSpec, register_metric, compute_at_junction, …,
|
| 130 |
+
# PROFILE_*, KNOWN_PROFILES, MetricsResult, aggregate_metrics)
|
| 131 |
+
# sont exposés depuis
|
| 132 |
+
# ``picarones.evaluation.{metric_registry, metric_hooks,
|
| 133 |
+
# metric_result}``. Comme pour le Lot A, les entrées sont
|
| 134 |
+
# retirées en même temps que les shims pour garder la table
|
| 135 |
+
# alignée avec l'arbre legacy réellement présent sur disque.
|
| 136 |
+
# ──────────────────────────────────────────────────────────
|
| 137 |
+
# Phase 4-ter (résiduel) — results
|
| 138 |
# ──────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
"picarones.core.results.BenchmarkResult": {
|
| 140 |
"canonical": "picarones.evaluation.benchmark_result.BenchmarkResult",
|
| 141 |
},
|
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
Couvre :
|
| 4 |
|
| 5 |
-
- :mod:`picarones.
|
| 6 |
sélection par profil, exécution avec gestion d'erreurs.
|
| 7 |
- :mod:`picarones.measurements.builtin_hooks` : enregistre les 12+12 hooks
|
| 8 |
historiques sur le profil ``standard``.
|
|
@@ -28,7 +28,7 @@ import pytest
|
|
| 28 |
|
| 29 |
class TestProfiles:
|
| 30 |
def test_known_profiles_complete(self):
|
| 31 |
-
from picarones.
|
| 32 |
|
| 33 |
assert KNOWN_PROFILES == frozenset({
|
| 34 |
"minimal", "standard", "philological", "diagnostics",
|
|
@@ -36,20 +36,20 @@ class TestProfiles:
|
|
| 36 |
})
|
| 37 |
|
| 38 |
def test_validate_profile_accepts_known(self):
|
| 39 |
-
from picarones.
|
| 40 |
|
| 41 |
for p in ["minimal", "standard", "philological", "diagnostics",
|
| 42 |
"economics", "pipeline", "full"]:
|
| 43 |
validate_profile(p) # ne lève pas
|
| 44 |
|
| 45 |
def test_validate_profile_rejects_unknown(self):
|
| 46 |
-
from picarones.
|
| 47 |
|
| 48 |
with pytest.raises(ValueError, match="profil inconnu"):
|
| 49 |
validate_profile("philolagic")
|
| 50 |
|
| 51 |
def test_validate_profile_rejects_empty(self):
|
| 52 |
-
from picarones.
|
| 53 |
|
| 54 |
with pytest.raises(ValueError):
|
| 55 |
validate_profile("")
|
|
@@ -64,7 +64,7 @@ class TestBuiltinHooksRegistration:
|
|
| 64 |
def test_twelve_document_hooks_registered(self):
|
| 65 |
# Import déclenche l'enregistrement via décorateurs.
|
| 66 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 67 |
-
from picarones.
|
| 68 |
|
| 69 |
names = set(_all_document_hook_names())
|
| 70 |
expected = {
|
|
@@ -77,7 +77,7 @@ class TestBuiltinHooksRegistration:
|
|
| 77 |
|
| 78 |
def test_twelve_corpus_aggregators_registered(self):
|
| 79 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 80 |
-
from picarones.
|
| 81 |
|
| 82 |
names = set(_all_corpus_aggregator_names())
|
| 83 |
expected = {
|
|
@@ -90,7 +90,7 @@ class TestBuiltinHooksRegistration:
|
|
| 90 |
|
| 91 |
def test_standard_profile_activates_all_hooks(self):
|
| 92 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 93 |
-
from picarones.
|
| 94 |
select_corpus_aggregators, select_document_hooks,
|
| 95 |
)
|
| 96 |
|
|
@@ -101,7 +101,7 @@ class TestBuiltinHooksRegistration:
|
|
| 101 |
|
| 102 |
def test_minimal_profile_activates_zero_hooks(self):
|
| 103 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 104 |
-
from picarones.
|
| 105 |
select_corpus_aggregators, select_document_hooks,
|
| 106 |
)
|
| 107 |
|
|
@@ -115,7 +115,7 @@ class TestBuiltinHooksRegistration:
|
|
| 115 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 116 |
from dataclasses import fields
|
| 117 |
|
| 118 |
-
from picarones.
|
| 119 |
from picarones.core.results import DocumentResult
|
| 120 |
|
| 121 |
doc_fields = {f.name for f in fields(DocumentResult)}
|
|
@@ -129,7 +129,7 @@ class TestBuiltinHooksRegistration:
|
|
| 129 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 130 |
from dataclasses import fields
|
| 131 |
|
| 132 |
-
from picarones.
|
| 133 |
from picarones.core.results import EngineReport
|
| 134 |
|
| 135 |
report_fields = {f.name for f in fields(EngineReport)}
|
|
@@ -157,7 +157,7 @@ class _MockEngineResult:
|
|
| 157 |
|
| 158 |
class TestRunDocumentHooks:
|
| 159 |
def test_minimal_profile_returns_empty_dict(self):
|
| 160 |
-
from picarones.
|
| 161 |
|
| 162 |
result = run_document_hooks(
|
| 163 |
"minimal",
|
|
@@ -172,7 +172,7 @@ class TestRunDocumentHooks:
|
|
| 172 |
def test_hook_exception_does_not_propagate(self, caplog):
|
| 173 |
"""Un hook qui lève doit être loggé en warning, pas faire
|
| 174 |
échouer le calcul des autres hooks."""
|
| 175 |
-
import picarones.
|
| 176 |
|
| 177 |
# Crée un profil de test isolé via un hook qui lève
|
| 178 |
custom_profile_name = "standard"
|
|
@@ -205,7 +205,7 @@ class TestRunDocumentHooks:
|
|
| 205 |
def test_requires_success_skips_failed_ocr(self):
|
| 206 |
"""Un hook ``requires_success=True`` ne doit pas être appelé si
|
| 207 |
``ocr_result.success`` est False."""
|
| 208 |
-
import picarones.
|
| 209 |
|
| 210 |
called = []
|
| 211 |
|
|
@@ -233,7 +233,7 @@ class TestRunDocumentHooks:
|
|
| 233 |
def test_requires_token_confidences_skips_when_absent(self):
|
| 234 |
"""Un hook ``requires_token_confidences=True`` doit être sauté
|
| 235 |
quand ``ocr_result.token_confidences`` est None."""
|
| 236 |
-
import picarones.
|
| 237 |
|
| 238 |
called = []
|
| 239 |
|
|
@@ -299,7 +299,7 @@ class TestDecoratorIdempotence:
|
|
| 299 |
def test_register_same_func_twice_is_silent(self):
|
| 300 |
"""Ré-import d'un module en test ne doit pas lever sur le
|
| 301 |
décorateur déjà appliqué."""
|
| 302 |
-
from picarones.
|
| 303 |
|
| 304 |
@register_document_metric(
|
| 305 |
name="reimport_test_chantier2",
|
|
@@ -319,7 +319,7 @@ class TestDecoratorIdempotence:
|
|
| 319 |
assert result is _hook
|
| 320 |
|
| 321 |
def test_register_different_func_same_name_raises(self):
|
| 322 |
-
from picarones.
|
| 323 |
|
| 324 |
@register_document_metric(
|
| 325 |
name="conflict_test_chantier2",
|
|
@@ -339,7 +339,7 @@ class TestDecoratorIdempotence:
|
|
| 339 |
return None
|
| 340 |
|
| 341 |
def test_register_unknown_profile_raises(self):
|
| 342 |
-
from picarones.
|
| 343 |
|
| 344 |
with pytest.raises(ValueError, match="profils inconnus"):
|
| 345 |
@register_document_metric(
|
|
|
|
| 2 |
|
| 3 |
Couvre :
|
| 4 |
|
| 5 |
+
- :mod:`picarones.evaluation.metric_hooks` : profils, registre, décorateurs,
|
| 6 |
sélection par profil, exécution avec gestion d'erreurs.
|
| 7 |
- :mod:`picarones.measurements.builtin_hooks` : enregistre les 12+12 hooks
|
| 8 |
historiques sur le profil ``standard``.
|
|
|
|
| 28 |
|
| 29 |
class TestProfiles:
|
| 30 |
def test_known_profiles_complete(self):
|
| 31 |
+
from picarones.evaluation.metric_hooks import KNOWN_PROFILES
|
| 32 |
|
| 33 |
assert KNOWN_PROFILES == frozenset({
|
| 34 |
"minimal", "standard", "philological", "diagnostics",
|
|
|
|
| 36 |
})
|
| 37 |
|
| 38 |
def test_validate_profile_accepts_known(self):
|
| 39 |
+
from picarones.evaluation.metric_hooks import validate_profile
|
| 40 |
|
| 41 |
for p in ["minimal", "standard", "philological", "diagnostics",
|
| 42 |
"economics", "pipeline", "full"]:
|
| 43 |
validate_profile(p) # ne lève pas
|
| 44 |
|
| 45 |
def test_validate_profile_rejects_unknown(self):
|
| 46 |
+
from picarones.evaluation.metric_hooks import validate_profile
|
| 47 |
|
| 48 |
with pytest.raises(ValueError, match="profil inconnu"):
|
| 49 |
validate_profile("philolagic")
|
| 50 |
|
| 51 |
def test_validate_profile_rejects_empty(self):
|
| 52 |
+
from picarones.evaluation.metric_hooks import validate_profile
|
| 53 |
|
| 54 |
with pytest.raises(ValueError):
|
| 55 |
validate_profile("")
|
|
|
|
| 64 |
def test_twelve_document_hooks_registered(self):
|
| 65 |
# Import déclenche l'enregistrement via décorateurs.
|
| 66 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 67 |
+
from picarones.evaluation.metric_hooks import _all_document_hook_names
|
| 68 |
|
| 69 |
names = set(_all_document_hook_names())
|
| 70 |
expected = {
|
|
|
|
| 77 |
|
| 78 |
def test_twelve_corpus_aggregators_registered(self):
|
| 79 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 80 |
+
from picarones.evaluation.metric_hooks import _all_corpus_aggregator_names
|
| 81 |
|
| 82 |
names = set(_all_corpus_aggregator_names())
|
| 83 |
expected = {
|
|
|
|
| 90 |
|
| 91 |
def test_standard_profile_activates_all_hooks(self):
|
| 92 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 93 |
+
from picarones.evaluation.metric_hooks import (
|
| 94 |
select_corpus_aggregators, select_document_hooks,
|
| 95 |
)
|
| 96 |
|
|
|
|
| 101 |
|
| 102 |
def test_minimal_profile_activates_zero_hooks(self):
|
| 103 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 104 |
+
from picarones.evaluation.metric_hooks import (
|
| 105 |
select_corpus_aggregators, select_document_hooks,
|
| 106 |
)
|
| 107 |
|
|
|
|
| 115 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 116 |
from dataclasses import fields
|
| 117 |
|
| 118 |
+
from picarones.evaluation.metric_hooks import select_document_hooks
|
| 119 |
from picarones.core.results import DocumentResult
|
| 120 |
|
| 121 |
doc_fields = {f.name for f in fields(DocumentResult)}
|
|
|
|
| 129 |
import picarones.measurements.builtin_hooks # noqa: F401
|
| 130 |
from dataclasses import fields
|
| 131 |
|
| 132 |
+
from picarones.evaluation.metric_hooks import select_corpus_aggregators
|
| 133 |
from picarones.core.results import EngineReport
|
| 134 |
|
| 135 |
report_fields = {f.name for f in fields(EngineReport)}
|
|
|
|
| 157 |
|
| 158 |
class TestRunDocumentHooks:
|
| 159 |
def test_minimal_profile_returns_empty_dict(self):
|
| 160 |
+
from picarones.evaluation.metric_hooks import run_document_hooks
|
| 161 |
|
| 162 |
result = run_document_hooks(
|
| 163 |
"minimal",
|
|
|
|
| 172 |
def test_hook_exception_does_not_propagate(self, caplog):
|
| 173 |
"""Un hook qui lève doit être loggé en warning, pas faire
|
| 174 |
échouer le calcul des autres hooks."""
|
| 175 |
+
import picarones.evaluation.metric_hooks as mh
|
| 176 |
|
| 177 |
# Crée un profil de test isolé via un hook qui lève
|
| 178 |
custom_profile_name = "standard"
|
|
|
|
| 205 |
def test_requires_success_skips_failed_ocr(self):
|
| 206 |
"""Un hook ``requires_success=True`` ne doit pas être appelé si
|
| 207 |
``ocr_result.success`` est False."""
|
| 208 |
+
import picarones.evaluation.metric_hooks as mh
|
| 209 |
|
| 210 |
called = []
|
| 211 |
|
|
|
|
| 233 |
def test_requires_token_confidences_skips_when_absent(self):
|
| 234 |
"""Un hook ``requires_token_confidences=True`` doit être sauté
|
| 235 |
quand ``ocr_result.token_confidences`` est None."""
|
| 236 |
+
import picarones.evaluation.metric_hooks as mh
|
| 237 |
|
| 238 |
called = []
|
| 239 |
|
|
|
|
| 299 |
def test_register_same_func_twice_is_silent(self):
|
| 300 |
"""Ré-import d'un module en test ne doit pas lever sur le
|
| 301 |
décorateur déjà appliqué."""
|
| 302 |
+
from picarones.evaluation.metric_hooks import register_document_metric
|
| 303 |
|
| 304 |
@register_document_metric(
|
| 305 |
name="reimport_test_chantier2",
|
|
|
|
| 319 |
assert result is _hook
|
| 320 |
|
| 321 |
def test_register_different_func_same_name_raises(self):
|
| 322 |
+
from picarones.evaluation.metric_hooks import register_document_metric
|
| 323 |
|
| 324 |
@register_document_metric(
|
| 325 |
name="conflict_test_chantier2",
|
|
|
|
| 339 |
return None
|
| 340 |
|
| 341 |
def test_register_unknown_profile_raises(self):
|
| 342 |
+
from picarones.evaluation.metric_hooks import register_document_metric
|
| 343 |
|
| 344 |
with pytest.raises(ValueError, match="profils inconnus"):
|
| 345 |
@register_document_metric(
|
|
@@ -292,25 +292,25 @@ class TestPipelineSpecLoaderApi:
|
|
| 292 |
|
| 293 |
|
| 294 |
# ──────────────────────────────────────────────────────────────────────────
|
| 295 |
-
# 7. picarones.
|
| 296 |
# ──────────────────────────────────────────────────────────────────────────
|
| 297 |
|
| 298 |
|
| 299 |
class TestMetricRegistryApi:
|
| 300 |
def test_metric_spec_class(self):
|
| 301 |
-
_assert_class("picarones.
|
| 302 |
|
| 303 |
@pytest.mark.parametrize("name", [
|
| 304 |
"register_metric", "get_metric", "all_metrics",
|
| 305 |
"select_metrics", "compute_at_junction",
|
| 306 |
])
|
| 307 |
def test_function_exists(self, name):
|
| 308 |
-
_assert_function("picarones.
|
| 309 |
|
| 310 |
def test_register_metric_keyword_only(self):
|
| 311 |
"""``register_metric`` est exclusivement keyword-only sur ``name``,
|
| 312 |
``input_types`` etc. — décorateur factory."""
|
| 313 |
-
from picarones.
|
| 314 |
sig = inspect.signature(register_metric)
|
| 315 |
for name in ["name", "input_types", "description"]:
|
| 316 |
assert name in sig.parameters, (
|
|
@@ -319,7 +319,7 @@ class TestMetricRegistryApi:
|
|
| 319 |
|
| 320 |
|
| 321 |
# ──────────────────────────────────────────────────────────────────────────
|
| 322 |
-
# 8. picarones.
|
| 323 |
# ──────────────────────────────────────────────────────────────────────────
|
| 324 |
|
| 325 |
|
|
@@ -330,14 +330,14 @@ class TestMetricHooksApi:
|
|
| 330 |
"PROFILE_FULL",
|
| 331 |
])
|
| 332 |
def test_profile_constant_exists(self, profile_name):
|
| 333 |
-
from picarones.
|
| 334 |
assert hasattr(metric_hooks, profile_name), (
|
| 335 |
f"Profil {profile_name} disparu"
|
| 336 |
)
|
| 337 |
assert isinstance(getattr(metric_hooks, profile_name), str)
|
| 338 |
|
| 339 |
def test_known_profiles_set(self):
|
| 340 |
-
from picarones.
|
| 341 |
|
| 342 |
assert isinstance(KNOWN_PROFILES, frozenset)
|
| 343 |
# Les 7 profils contractuels
|
|
@@ -347,7 +347,7 @@ class TestMetricHooksApi:
|
|
| 347 |
"DocumentMetricHook", "CorpusMetricAggregator",
|
| 348 |
])
|
| 349 |
def test_class_exists(self, name):
|
| 350 |
-
_assert_class("picarones.
|
| 351 |
|
| 352 |
@pytest.mark.parametrize("name", [
|
| 353 |
"validate_profile",
|
|
@@ -356,7 +356,7 @@ class TestMetricHooksApi:
|
|
| 356 |
"run_document_hooks", "run_corpus_aggregators",
|
| 357 |
])
|
| 358 |
def test_function_exists(self, name):
|
| 359 |
-
_assert_function("picarones.
|
| 360 |
|
| 361 |
|
| 362 |
# ──────────────────────────────────────────────────────────────────────────
|
|
@@ -502,8 +502,8 @@ class TestApiStableDoc:
|
|
| 502 |
"picarones.measurements.pipeline_benchmark",
|
| 503 |
"picarones.measurements.pipeline_comparison",
|
| 504 |
"picarones.measurements.pipeline_spec_loader",
|
| 505 |
-
"picarones.
|
| 506 |
-
"picarones.
|
| 507 |
"picarones.measurements.builtin_metrics",
|
| 508 |
"picarones.measurements.alto_metrics",
|
| 509 |
"picarones.web.jobs",
|
|
|
|
| 292 |
|
| 293 |
|
| 294 |
# ──────────────────────────────────────────────────────────────────────────
|
| 295 |
+
# 7. picarones.evaluation.metric_registry — registre typé (canonique)
|
| 296 |
# ──────────────────────────────────────────────────────────────────────────
|
| 297 |
|
| 298 |
|
| 299 |
class TestMetricRegistryApi:
|
| 300 |
def test_metric_spec_class(self):
|
| 301 |
+
_assert_class("picarones.evaluation.metric_registry", "MetricSpec")
|
| 302 |
|
| 303 |
@pytest.mark.parametrize("name", [
|
| 304 |
"register_metric", "get_metric", "all_metrics",
|
| 305 |
"select_metrics", "compute_at_junction",
|
| 306 |
])
|
| 307 |
def test_function_exists(self, name):
|
| 308 |
+
_assert_function("picarones.evaluation.metric_registry", name)
|
| 309 |
|
| 310 |
def test_register_metric_keyword_only(self):
|
| 311 |
"""``register_metric`` est exclusivement keyword-only sur ``name``,
|
| 312 |
``input_types`` etc. — décorateur factory."""
|
| 313 |
+
from picarones.evaluation.metric_registry import register_metric
|
| 314 |
sig = inspect.signature(register_metric)
|
| 315 |
for name in ["name", "input_types", "description"]:
|
| 316 |
assert name in sig.parameters, (
|
|
|
|
| 319 |
|
| 320 |
|
| 321 |
# ──────────────────────────────────────────────────────────────────────────
|
| 322 |
+
# 8. picarones.evaluation.metric_hooks — profils + registre de hooks (canonique)
|
| 323 |
# ──────────────────────────────────────────────────────────────────────────
|
| 324 |
|
| 325 |
|
|
|
|
| 330 |
"PROFILE_FULL",
|
| 331 |
])
|
| 332 |
def test_profile_constant_exists(self, profile_name):
|
| 333 |
+
from picarones.evaluation import metric_hooks
|
| 334 |
assert hasattr(metric_hooks, profile_name), (
|
| 335 |
f"Profil {profile_name} disparu"
|
| 336 |
)
|
| 337 |
assert isinstance(getattr(metric_hooks, profile_name), str)
|
| 338 |
|
| 339 |
def test_known_profiles_set(self):
|
| 340 |
+
from picarones.evaluation.metric_hooks import KNOWN_PROFILES
|
| 341 |
|
| 342 |
assert isinstance(KNOWN_PROFILES, frozenset)
|
| 343 |
# Les 7 profils contractuels
|
|
|
|
| 347 |
"DocumentMetricHook", "CorpusMetricAggregator",
|
| 348 |
])
|
| 349 |
def test_class_exists(self, name):
|
| 350 |
+
_assert_class("picarones.evaluation.metric_hooks", name)
|
| 351 |
|
| 352 |
@pytest.mark.parametrize("name", [
|
| 353 |
"validate_profile",
|
|
|
|
| 356 |
"run_document_hooks", "run_corpus_aggregators",
|
| 357 |
])
|
| 358 |
def test_function_exists(self, name):
|
| 359 |
+
_assert_function("picarones.evaluation.metric_hooks", name)
|
| 360 |
|
| 361 |
|
| 362 |
# ──────────────────────────────────────────────────────────────────────────
|
|
|
|
| 502 |
"picarones.measurements.pipeline_benchmark",
|
| 503 |
"picarones.measurements.pipeline_comparison",
|
| 504 |
"picarones.measurements.pipeline_spec_loader",
|
| 505 |
+
"picarones.evaluation.metric_registry",
|
| 506 |
+
"picarones.evaluation.metric_hooks",
|
| 507 |
"picarones.measurements.builtin_metrics",
|
| 508 |
"picarones.measurements.alto_metrics",
|
| 509 |
"picarones.web.jobs",
|
|
@@ -19,7 +19,7 @@ from __future__ import annotations
|
|
| 19 |
|
| 20 |
import pytest
|
| 21 |
|
| 22 |
-
from picarones.
|
| 23 |
MetricSpec,
|
| 24 |
all_metrics,
|
| 25 |
compute_at_junction,
|
|
@@ -126,7 +126,7 @@ class TestComputeAtJunction:
|
|
| 126 |
assert "cer" in out
|
| 127 |
finally:
|
| 128 |
# Nettoyage manuel — pas d'API publique, on écrit dans le dict.
|
| 129 |
-
from picarones.
|
| 130 |
|
| 131 |
_METRIC_REGISTRY.pop("_test_always_raises", None)
|
| 132 |
|
|
@@ -146,7 +146,7 @@ class TestComputeAtJunction:
|
|
| 146 |
skip_on_error=False,
|
| 147 |
)
|
| 148 |
finally:
|
| 149 |
-
from picarones.
|
| 150 |
|
| 151 |
_METRIC_REGISTRY.pop("_test_propagates", None)
|
| 152 |
|
|
@@ -211,7 +211,7 @@ class TestRegistrationGuards:
|
|
| 211 |
def _second(ref: str, hyp: str) -> float:
|
| 212 |
return 1.0
|
| 213 |
finally:
|
| 214 |
-
from picarones.
|
| 215 |
|
| 216 |
_METRIC_REGISTRY.pop("_test_duplicate", None)
|
| 217 |
|
|
@@ -232,7 +232,7 @@ class TestRegistrationGuards:
|
|
| 232 |
input_types=(ArtifactType.TEXT, ArtifactType.TEXT),
|
| 233 |
)(_func)
|
| 234 |
|
| 235 |
-
from picarones.
|
| 236 |
|
| 237 |
_METRIC_REGISTRY.pop("_test_idempotent", None)
|
| 238 |
|
|
|
|
| 19 |
|
| 20 |
import pytest
|
| 21 |
|
| 22 |
+
from picarones.evaluation.metric_registry import (
|
| 23 |
MetricSpec,
|
| 24 |
all_metrics,
|
| 25 |
compute_at_junction,
|
|
|
|
| 126 |
assert "cer" in out
|
| 127 |
finally:
|
| 128 |
# Nettoyage manuel — pas d'API publique, on écrit dans le dict.
|
| 129 |
+
from picarones.evaluation.metric_registry import _METRIC_REGISTRY
|
| 130 |
|
| 131 |
_METRIC_REGISTRY.pop("_test_always_raises", None)
|
| 132 |
|
|
|
|
| 146 |
skip_on_error=False,
|
| 147 |
)
|
| 148 |
finally:
|
| 149 |
+
from picarones.evaluation.metric_registry import _METRIC_REGISTRY
|
| 150 |
|
| 151 |
_METRIC_REGISTRY.pop("_test_propagates", None)
|
| 152 |
|
|
|
|
| 211 |
def _second(ref: str, hyp: str) -> float:
|
| 212 |
return 1.0
|
| 213 |
finally:
|
| 214 |
+
from picarones.evaluation.metric_registry import _METRIC_REGISTRY
|
| 215 |
|
| 216 |
_METRIC_REGISTRY.pop("_test_duplicate", None)
|
| 217 |
|
|
|
|
| 232 |
input_types=(ArtifactType.TEXT, ArtifactType.TEXT),
|
| 233 |
)(_func)
|
| 234 |
|
| 235 |
+
from picarones.evaluation.metric_registry import _METRIC_REGISTRY
|
| 236 |
|
| 237 |
_METRIC_REGISTRY.pop("_test_idempotent", None)
|
| 238 |
|
|
@@ -19,7 +19,7 @@ suppression des analyses via ``drop_analyses=True``.
|
|
| 19 |
|
| 20 |
from __future__ import annotations
|
| 21 |
|
| 22 |
-
from picarones.
|
| 23 |
from picarones.core.results import DocumentResult
|
| 24 |
|
| 25 |
|
|
|
|
| 19 |
|
| 20 |
from __future__ import annotations
|
| 21 |
|
| 22 |
+
from picarones.evaluation.metric_result import MetricsResult
|
| 23 |
from picarones.core.results import DocumentResult
|
| 24 |
|
| 25 |
|
|
@@ -19,7 +19,7 @@ from __future__ import annotations
|
|
| 19 |
from unittest import mock
|
| 20 |
|
| 21 |
|
| 22 |
-
from picarones.
|
| 23 |
from picarones.measurements import metrics as metrics_module
|
| 24 |
from picarones.measurements.metrics import compute_metrics
|
| 25 |
|
|
|
|
| 19 |
from unittest import mock
|
| 20 |
|
| 21 |
|
| 22 |
+
from picarones.evaluation.metric_result import MetricsResult, aggregate_metrics
|
| 23 |
from picarones.measurements import metrics as metrics_module
|
| 24 |
from picarones.measurements.metrics import compute_metrics
|
| 25 |
|
|
@@ -232,8 +232,8 @@ class TestCompute:
|
|
| 232 |
|
| 233 |
class TestNoGlobalSingleton:
|
| 234 |
def test_two_registries_are_independent(self) -> None:
|
| 235 |
-
"""Différence cruciale avec
|
| 236 |
-
``picarones.
|
| 237 |
deux ``MetricRegistry()`` ne se partagent rien."""
|
| 238 |
reg_a = MetricRegistry()
|
| 239 |
reg_b = MetricRegistry()
|
|
|
|
| 232 |
|
| 233 |
class TestNoGlobalSingleton:
|
| 234 |
def test_two_registries_are_independent(self) -> None:
|
| 235 |
+
"""Différence cruciale avec
|
| 236 |
+
``picarones.evaluation.metric_registry`` qui a un dict global :
|
| 237 |
deux ``MetricRegistry()`` ne se partagent rien."""
|
| 238 |
reg_a = MetricRegistry()
|
| 239 |
reg_b = MetricRegistry()
|
|
@@ -27,7 +27,7 @@ from picarones.measurements.alto_metrics import (
|
|
| 27 |
extract_text_from_alto,
|
| 28 |
)
|
| 29 |
from picarones.core.corpus import AltoGT, Document, GTLevel, TextGT
|
| 30 |
-
from picarones.
|
| 31 |
from picarones.domain.artifacts import ArtifactType
|
| 32 |
from picarones.domain.module_protocol import BaseModule
|
| 33 |
from picarones.evaluation.pipeline import (
|
|
|
|
| 27 |
extract_text_from_alto,
|
| 28 |
)
|
| 29 |
from picarones.core.corpus import AltoGT, Document, GTLevel, TextGT
|
| 30 |
+
from picarones.evaluation.metric_registry import compute_at_junction, select_metrics
|
| 31 |
from picarones.domain.artifacts import ArtifactType
|
| 32 |
from picarones.domain.module_protocol import BaseModule
|
| 33 |
from picarones.evaluation.pipeline import (
|
|
@@ -32,7 +32,7 @@ from typing import Any
|
|
| 32 |
import pytest
|
| 33 |
|
| 34 |
from picarones.core.corpus import AltoGT, Document, GTLevel, TextGT
|
| 35 |
-
from picarones.
|
| 36 |
from picarones.domain.artifacts import ArtifactType
|
| 37 |
from picarones.domain.module_protocol import BaseModule
|
| 38 |
from picarones.evaluation.pipeline import (
|
|
|
|
| 32 |
import pytest
|
| 33 |
|
| 34 |
from picarones.core.corpus import AltoGT, Document, GTLevel, TextGT
|
| 35 |
+
from picarones.evaluation.metric_registry import select_metrics
|
| 36 |
from picarones.domain.artifacts import ArtifactType
|
| 37 |
from picarones.domain.module_protocol import BaseModule
|
| 38 |
from picarones.evaluation.pipeline import (
|
|
@@ -31,7 +31,7 @@ from __future__ import annotations
|
|
| 31 |
|
| 32 |
import pytest
|
| 33 |
|
| 34 |
-
from picarones.
|
| 35 |
from picarones.domain.artifacts import ArtifactType
|
| 36 |
from picarones.measurements.ner import Entity, compute_ner_metrics, ner_f1
|
| 37 |
|
|
|
|
| 31 |
|
| 32 |
import pytest
|
| 33 |
|
| 34 |
+
from picarones.evaluation.metric_registry import compute_at_junction, select_metrics
|
| 35 |
from picarones.domain.artifacts import ArtifactType
|
| 36 |
from picarones.measurements.ner import Entity, compute_ner_metrics, ner_f1
|
| 37 |
|
|
@@ -28,7 +28,7 @@ from __future__ import annotations
|
|
| 28 |
|
| 29 |
import pytest
|
| 30 |
|
| 31 |
-
from picarones.
|
| 32 |
from picarones.domain.artifacts import ArtifactType
|
| 33 |
from picarones.measurements.readability import (
|
| 34 |
count_sentences,
|
|
@@ -236,7 +236,7 @@ class TestRegistryIntegration:
|
|
| 236 |
assert "flesch_delta_en" in names
|
| 237 |
|
| 238 |
def test_registered_function_returns_same_as_direct_call(self) -> None:
|
| 239 |
-
from picarones.
|
| 240 |
|
| 241 |
gt = "Je vous envoie cette missive afin de vous informer."
|
| 242 |
ocr = "Je vous écris une lettre. Voici la situation."
|
|
|
|
| 28 |
|
| 29 |
import pytest
|
| 30 |
|
| 31 |
+
from picarones.evaluation.metric_registry import select_metrics
|
| 32 |
from picarones.domain.artifacts import ArtifactType
|
| 33 |
from picarones.measurements.readability import (
|
| 34 |
count_sentences,
|
|
|
|
| 236 |
assert "flesch_delta_en" in names
|
| 237 |
|
| 238 |
def test_registered_function_returns_same_as_direct_call(self) -> None:
|
| 239 |
+
from picarones.evaluation.metric_registry import compute_at_junction
|
| 240 |
|
| 241 |
gt = "Je vous envoie cette missive afin de vous informer."
|
| 242 |
ocr = "Je vous écris une lettre. Voici la situation."
|
|
@@ -26,7 +26,7 @@ from __future__ import annotations
|
|
| 26 |
|
| 27 |
import pytest
|
| 28 |
|
| 29 |
-
from picarones.
|
| 30 |
from picarones.domain.artifacts import ArtifactType
|
| 31 |
from picarones.measurements.reading_order import (
|
| 32 |
compute_reading_order_metrics,
|
|
|
|
| 26 |
|
| 27 |
import pytest
|
| 28 |
|
| 29 |
+
from picarones.evaluation.metric_registry import compute_at_junction, select_metrics
|
| 30 |
from picarones.domain.artifacts import ArtifactType
|
| 31 |
from picarones.measurements.reading_order import (
|
| 32 |
compute_reading_order_metrics,
|
|
@@ -23,7 +23,7 @@ from __future__ import annotations
|
|
| 23 |
|
| 24 |
import pytest
|
| 25 |
|
| 26 |
-
from picarones.
|
| 27 |
from picarones.domain.artifacts import ArtifactType
|
| 28 |
from picarones.measurements.unicode_blocks import (
|
| 29 |
compute_unicode_block_accuracy,
|
|
|
|
| 23 |
|
| 24 |
import pytest
|
| 25 |
|
| 26 |
+
from picarones.evaluation.metric_registry import compute_at_junction, select_metrics
|
| 27 |
from picarones.domain.artifacts import ArtifactType
|
| 28 |
from picarones.measurements.unicode_blocks import (
|
| 29 |
compute_unicode_block_accuracy,
|
|
@@ -34,7 +34,7 @@ from picarones.measurements.abbreviations import (
|
|
| 34 |
compute_abbreviation_metrics,
|
| 35 |
detect_abbreviations,
|
| 36 |
)
|
| 37 |
-
from picarones.
|
| 38 |
from picarones.domain.artifacts import ArtifactType
|
| 39 |
|
| 40 |
|
|
|
|
| 34 |
compute_abbreviation_metrics,
|
| 35 |
detect_abbreviations,
|
| 36 |
)
|
| 37 |
+
from picarones.evaluation.metric_registry import compute_at_junction, select_metrics
|
| 38 |
from picarones.domain.artifacts import ArtifactType
|
| 39 |
|
| 40 |
|
|
@@ -31,7 +31,7 @@ from __future__ import annotations
|
|
| 31 |
|
| 32 |
import pytest
|
| 33 |
|
| 34 |
-
from picarones.
|
| 35 |
from picarones.domain.artifacts import ArtifactType
|
| 36 |
from picarones.measurements.mufi import (
|
| 37 |
compute_mufi_coverage,
|
|
|
|
| 31 |
|
| 32 |
import pytest
|
| 33 |
|
| 34 |
+
from picarones.evaluation.metric_registry import compute_at_junction, select_metrics
|
| 35 |
from picarones.domain.artifacts import ArtifactType
|
| 36 |
from picarones.measurements.mufi import (
|
| 37 |
compute_mufi_coverage,
|
|
@@ -38,7 +38,7 @@ from picarones.measurements.early_modern_typography import (
|
|
| 38 |
early_modern_preservation,
|
| 39 |
get_category,
|
| 40 |
)
|
| 41 |
-
from picarones.
|
| 42 |
from picarones.domain.artifacts import ArtifactType
|
| 43 |
|
| 44 |
|
|
|
|
| 38 |
early_modern_preservation,
|
| 39 |
get_category,
|
| 40 |
)
|
| 41 |
+
from picarones.evaluation.metric_registry import compute_at_junction, select_metrics
|
| 42 |
from picarones.domain.artifacts import ArtifactType
|
| 43 |
|
| 44 |
|
|
@@ -35,7 +35,7 @@ from __future__ import annotations
|
|
| 35 |
|
| 36 |
import pytest
|
| 37 |
|
| 38 |
-
from picarones.
|
| 39 |
from picarones.measurements.modern_archives import (
|
| 40 |
ADDRESS,
|
| 41 |
ADMINISTRATIVE,
|
|
|
|
| 35 |
|
| 36 |
import pytest
|
| 37 |
|
| 38 |
+
from picarones.evaluation.metric_registry import compute_at_junction, select_metrics
|
| 39 |
from picarones.measurements.modern_archives import (
|
| 40 |
ADDRESS,
|
| 41 |
ADMINISTRATIVE,
|
|
@@ -21,7 +21,7 @@ from __future__ import annotations
|
|
| 21 |
|
| 22 |
import pytest
|
| 23 |
|
| 24 |
-
from picarones.
|
| 25 |
from picarones.domain.artifacts import ArtifactType
|
| 26 |
from picarones.evaluation.metrics.roman_numerals import (
|
| 27 |
ALL_STATUSES,
|
|
|
|
| 21 |
|
| 22 |
import pytest
|
| 23 |
|
| 24 |
+
from picarones.evaluation.metric_registry import compute_at_junction, select_metrics
|
| 25 |
from picarones.domain.artifacts import ArtifactType
|
| 26 |
from picarones.evaluation.metrics.roman_numerals import (
|
| 27 |
ALL_STATUSES,
|
|
@@ -179,7 +179,7 @@ class TestRealisticCase:
|
|
| 179 |
|
| 180 |
class TestRegistry:
|
| 181 |
def test_metric_registered(self) -> None:
|
| 182 |
-
from picarones.
|
| 183 |
from picarones.domain.artifacts import ArtifactType
|
| 184 |
|
| 185 |
metrics = select_metrics(
|
|
@@ -198,7 +198,7 @@ class TestRegistry:
|
|
| 198 |
assert v == 0.0
|
| 199 |
|
| 200 |
def test_metric_via_compute_at_junction(self) -> None:
|
| 201 |
-
from picarones.
|
| 202 |
from picarones.domain.artifacts import ArtifactType
|
| 203 |
|
| 204 |
results = compute_at_junction(
|
|
|
|
| 179 |
|
| 180 |
class TestRegistry:
|
| 181 |
def test_metric_registered(self) -> None:
|
| 182 |
+
from picarones.evaluation.metric_registry import select_metrics
|
| 183 |
from picarones.domain.artifacts import ArtifactType
|
| 184 |
|
| 185 |
metrics = select_metrics(
|
|
|
|
| 198 |
assert v == 0.0
|
| 199 |
|
| 200 |
def test_metric_via_compute_at_junction(self) -> None:
|
| 201 |
+
from picarones.evaluation.metric_registry import compute_at_junction
|
| 202 |
from picarones.domain.artifacts import ArtifactType
|
| 203 |
|
| 204 |
results = compute_at_junction(
|
|
@@ -223,7 +223,7 @@ class TestRealistic:
|
|
| 223 |
|
| 224 |
class TestRegistry:
|
| 225 |
def test_strict_and_value_metrics_registered(self) -> None:
|
| 226 |
-
from picarones.
|
| 227 |
from picarones.domain.artifacts import ArtifactType
|
| 228 |
|
| 229 |
metrics = select_metrics((ArtifactType.TEXT, ArtifactType.TEXT))
|
|
@@ -243,7 +243,7 @@ class TestRegistry:
|
|
| 243 |
assert value == 1.0
|
| 244 |
|
| 245 |
def test_metric_via_compute_at_junction(self) -> None:
|
| 246 |
-
from picarones.
|
| 247 |
from picarones.domain.artifacts import ArtifactType
|
| 248 |
|
| 249 |
results = compute_at_junction(
|
|
|
|
| 223 |
|
| 224 |
class TestRegistry:
|
| 225 |
def test_strict_and_value_metrics_registered(self) -> None:
|
| 226 |
+
from picarones.evaluation.metric_registry import select_metrics
|
| 227 |
from picarones.domain.artifacts import ArtifactType
|
| 228 |
|
| 229 |
metrics = select_metrics((ArtifactType.TEXT, ArtifactType.TEXT))
|
|
|
|
| 243 |
assert value == 1.0
|
| 244 |
|
| 245 |
def test_metric_via_compute_at_junction(self) -> None:
|
| 246 |
+
from picarones.evaluation.metric_registry import compute_at_junction
|
| 247 |
from picarones.domain.artifacts import ArtifactType
|
| 248 |
|
| 249 |
results = compute_at_junction(
|