Spaces:
Sleeping
Sleeping
Claude
refactor(audit): Phase 3.1 + Phase 5 — code mort robustness + violations archi
1d1017e unverified | """Phase 5.3 audit code-quality — ``picarones.register_default_metrics`` | |
| est exposée comme API publique testable, en remplacement du | |
| side-effect import opaque qui était en tête de ``picarones/__init__.py``. | |
| Avant la Phase 5.3, le ``__init__`` contenait : | |
| .. code-block:: python | |
| import picarones.evaluation.metrics as _trigger_metric_registration | |
| Côté caller, l'enregistrement des ~37 métriques + ~25 agrégateurs | |
| corpus-level était implicite — pas de point d'entrée nommé, pas de | |
| test direct. Désormais : | |
| - ``register_default_metrics()`` est exposée dans ``picarones.__all__``. | |
| - L'auto-déclenchement en fin de ``__init__`` reste pour la | |
| rétrocompat (``from picarones import register_metric`` doit | |
| marcher hors-test sans setup explicite). | |
| - L'opération est idempotente (appel multiple sans effet de bord | |
| grâce au cache ``sys.modules``). | |
| """ | |
| from __future__ import annotations | |
| def test_register_default_metrics_is_public_api() -> None: | |
| """La fonction est exportée dans ``picarones.__all__``.""" | |
| import picarones | |
| assert "register_default_metrics" in picarones.__all__ | |
| assert callable(picarones.register_default_metrics) | |
| def test_register_default_metrics_is_idempotent() -> None: | |
| """Plusieurs appels successifs n'ont pas d'effet de bord — | |
| le module ``picarones.evaluation.metrics`` est cache par | |
| ``sys.modules`` après le premier import. | |
| """ | |
| import picarones | |
| from picarones.evaluation.metric_registry import all_metrics | |
| snapshot_before = len(all_metrics()) | |
| picarones.register_default_metrics() | |
| picarones.register_default_metrics() | |
| picarones.register_default_metrics() | |
| snapshot_after = len(all_metrics()) | |
| assert snapshot_before == snapshot_after, ( | |
| f"register_default_metrics non idempotent : {snapshot_before} → " | |
| f"{snapshot_after} métriques. Le cache sys.modules devrait " | |
| f"empêcher tout doublon." | |
| ) | |
| def test_auto_trigger_loads_evaluation_metrics_submodules() -> None: | |
| """Le simple ``import picarones`` doit charger les sous-modules | |
| listés dans ``picarones/evaluation/metrics/__init__.py`` (ce qui | |
| déclenche leurs ``@register_metric`` éventuels). | |
| Limitation connue (drift pré-existant identifié par l'audit | |
| code-quality, hors scope Phase 5.3) : tous les modules | |
| ``@register_metric`` ne sont pas dans le ``__init__``. | |
| L'enregistrement complet des ~37 métriques passe par | |
| ``builtin_hooks`` et ``philological_hooks`` qui sont eux-mêmes | |
| chargés à la demande par ``BenchmarkService``. Ce test vérifie | |
| uniquement l'invariant minimal : les modules **explicitement** | |
| listés sont bien chargés. | |
| """ | |
| import sys | |
| import picarones # noqa: F401 — déclenche l'auto-import | |
| # ``picarones.evaluation.metrics`` doit être chargé. | |
| assert "picarones.evaluation.metrics" in sys.modules, ( | |
| "Le module ``picarones.evaluation.metrics`` n'est pas dans " | |
| "``sys.modules`` après ``import picarones`` — " | |
| "``register_default_metrics`` ne déclenche plus l'import." | |
| ) | |
| # Et au moins un sous-module concret listé dans son __init__. | |
| assert "picarones.evaluation.metrics.confusion" in sys.modules | |
| def test_register_default_metrics_signature_takes_no_args() -> None: | |
| """Contrat d'API : appelable sans argument.""" | |
| import inspect | |
| import picarones | |
| sig = inspect.signature(picarones.register_default_metrics) | |
| assert len(sig.parameters) == 0, ( | |
| f"register_default_metrics doit être sans argument, signature : " | |
| f"{sig}" | |
| ) | |
| assert sig.return_annotation in (None, type(None), "None"), ( | |
| f"register_default_metrics doit retourner ``None``, " | |
| f"annotation actuelle : {sig.return_annotation}" | |
| ) | |