Skip to content

test: fiabilise et raccourcit la suite pytest (fix crash eccodes)#22

Merged
CyrilJl merged 6 commits into
mainfrom
ci/shorten-tests
Jul 8, 2026
Merged

test: fiabilise et raccourcit la suite pytest (fix crash eccodes)#22
CyrilJl merged 6 commits into
mainfrom
ci/shorten-tests

Conversation

@CyrilJl

@CyrilJl CyrilJl commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Contexte

La GitHub Action Run Pytest échouait de façon récurrente. L'audit a révélé trois causes indépendantes :

  1. Crash C non déterministe (cause principale, aussi présente sur main).
    set_grib_defs() appelait eccodes.codes_context_delete() à chaque changement de définitions GRIB, ce qui provoquait :

    Fatal Python error: Aborted / Segmentation fault
      gribapi.grib_context_delete
      meteofetch/_misc.py:118 set_grib_defs
    

    Crash non rattrapable par try/except, dépendant de la version d'eccodes/du timing.

  2. Durée excessive (>40 min) → annulation du runner. Téléchargement de vraies données pour 11 modèles MF × 2 jeux de définitions × tous leurs paquets (4–11) × 2 échéances → runs coupés (runner received a shutdown signal), remontés en échec.

  3. Assertion assert ds.mean() < 1 fragile. En mode test, elle exigeait qu'aucun champ ne soit 100 % manquant ; or certains champs ECMWF sont légitimement très masqués (vsw, zos, sithick) et peuvent atteindre 1.0 selon le run → échecs intermittents de test_aifs/test_ifs.

Changements

  • meteofetch/_misc.py : suppression de eccodes.codes_context_delete() (et de l'import eccodes devenu inutile). La lecture GRIB a lieu dans des processus enfants (multiprocessing.Pool) qui lisent ECCODES_DEFINITION_PATH à leur init ; réinitialiser le contexte du parent est inutile et fatal. Vérifié : la bascule eccodes↔meteofrance décode toujours correctement (mwd/swhMDPS/SHWW).
  • tests/test_models.py : un seul paquet représentatif testé par modèle Météo-France ; échéances limitées via monkeypatch (plus de mutation permanente de classe, attribut picklable) ; assertion au niveau dataset (« au moins un champ contient des données réelles ») ; test_aifs/test_ifs fusionnés en test_ecmwf_models paramétré.
  • pyproject.toml : enregistrement du marker pytest availability (supprime le warning).
  • .github/workflows/pytest.yml : timeout-minutes: 30 et fail-fast: false.

Validation CI

workflow_dispatch sur ci/shorten-tests : 61 passed, ubuntu 12m24s / macos 12m35s (contre >40 min sans jamais aboutir).

🤖 Generated with Claude Code

CyrilJl and others added 6 commits July 8, 2026 22:42
La CI "Run Pytest" échouait pour deux raisons indépendantes :

1. Durée (>40 min) — le job téléchargeait de vraies données GRIB pour
   11 modèles MF × 2 jeux de définitions × TOUS leurs paquets (4 à 11) ×
   2 échéances, plus les 2 modèles ECMWF. Les runs finissaient annulés
   ("runner received a shutdown signal"), remontés comme échec.

2. Assertion fragile `ds.mean() < 1` par champ — en mode test chaque champ
   vaut son masque isnull(), donc mean() = fraction de valeurs manquantes.
   Certains champs ECMWF sont légitimement masqués à ~100 % sur les 2 échéances
   échantillonnées (humidité du sol, variables océaniques) : selon le run, un
   champ peut être entièrement NaN → l'assertion échoue de façon non
   déterministe (d'où les échecs intermittents de test_aifs/test_ifs).

Changements :
- un seul paquet représentatif testé par modèle MF (au lieu de tous) ;
- limitation des échéances via monkeypatch (plus de mutation permanente de
  classe ; l'attribut reste sur la classe réelle, donc picklable par le Pool) ;
- assertion au niveau dataset : au moins un champ doit contenir des données
  réelles, ce qui détecte un pipeline cassé sans être sensible aux champs
  masqués individuels ;
- marker pytest `availability` enregistré (supprime le warning) ;
- timeout-minutes: 30 + fail-fast: false sur le job CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rpréteur

En CI, le changement de définitions GRIB provoquait un crash C non rattrapable :

  Fatal Python error: Aborted / Segmentation fault
    gribapi.grib_context_delete
    meteofetch/_misc.py:118 set_grib_defs -> eccodes.codes_context_delete()

La lecture des GRIBs a lieu dans des processus enfants (multiprocessing.Pool)
qui lisent ECCODES_DEFINITION_PATH à leur initialisation ; réinitialiser le
contexte eccodes du parent est donc inutile et dangereux. Vérifié localement :
le décodage bascule correctement entre définitions eccodes (mwd, swh, …) et
meteofrance (MDPS, SHWW, …) sans cet appel. Import eccodes devenu inutile retiré.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ajoute un déclencheur pull_request (en plus de workflow_dispatch) pour que la
suite pytest s'exécute automatiquement sur les PR ciblant main. Les tests ne
requièrent aucun secret (téléchargement de données publiques), donc les PR
issues de forks sont couvertes également.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Option pytest --full (tests/conftest.py) : bascule entre suite partielle
  (un paquet représentatif par modèle Météo-France) et suite exhaustive
  (tous les paquets).
- Workflow : les PR déclenchent la suite partielle ; le lancement manuel
  (workflow_dispatch, input "full" par défaut à true) exécute --full.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ests

Un push authentifié par le GITHUB_TOKEN ne relance pas les workflows : le
commit d'auto-formatage laissait donc les checks `test` absents du commit
final, bloquant le merge sous branch protection. Le workflow pousse désormais
avec le secret CI_PAT (repli sur github.token si absent, ex. PR de fork).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@CyrilJl
CyrilJl merged commit 2102643 into main Jul 8, 2026
3 checks passed
CyrilJl pushed a commit to miki4iaml/MeteoFetch that referenced this pull request Jul 9, 2026
La PR proposait plusieurs changements dans _misc.py. Après les correctifs récents sur eccodes, je garde seulement les deux points qui restent utiles :

- geo_encode_cf() copie la DataArray sans dupliquer les données sous-jacentes, pour éviter de modifier l'objet passé en argument ;

- set_test_mode() passe par le logger du package au lieu d'écrire directement sur stdout.

Je laisse set_grib_defs() aligné avec main. Remettre codes_context_delete(), même derrière un lock, réintroduirait le crash eccodes corrigé dans CyrilJl#22.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants