Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/ml-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,13 @@ jobs:
# depends on numpy/scipy/scikit-learn which are already installed --
# it is listed so the M5 suite actually RUNS here rather than being
# skipped, which would make its assertions a control never shown firing.
pip install numpy pandas scipy scikit-learn arch pytest pyarrow xgboost joblib hmmlearn
# pyarrow >= 26 (#21764) : sous 26, le noyau ipykernel meurt
# (Unhandled SIGSEGV) au chargement de pyarrow (chaine torch ->
# dask -> xarray -> pandas -> pyarrow). Mesure : 24.0.0 crash,
# 26.0.0 OK ; l'image portait 25.0.1, non mesure.
pip install numpy pandas scipy scikit-learn arch pytest "pyarrow>=26" xgboost joblib hmmlearn
# Version RESOLUE affichee : un plancher non affiche n'est pas prouve.
python -c "import pyarrow; print('pyarrow resolu:', pyarrow.__version__)"
pip install torch --index-url https://download.pytorch.org/whl/cpu

- name: Borner les threads au quota CPU du conteneur
Expand Down
39 changes: 38 additions & 1 deletion .github/workflows/scripts-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ on:
# distinct) -- meme classe que #18392/#13746 : sans cette entree, une PR
# qui ne touche que le service n'executerait AUCUNE jambe pytest.
- 'MyIA.AI.Notebooks/GameTheory/gateway/**'
# #21764 : le step bout-en-bout execute CE carnet en papermill ; sans
# cette entree, le garde ne declenche que sur les PR suivantes qui
# touchent scripts/** et jamais sur la PR du carnet lui-meme (angle mort
# mesure par la relecture tierce de #21783). Miroir sous pull_request:
# ci-dessous.
- 'MyIA.AI.Notebooks/GameTheory/GameTheory-06-EvolutionTrust-Python.ipynb'
# #21100 pli 0 Origami (Kernel Gateway) : le coeur du service Z3 Sudoku
# vit hors scripts/** (Z3-API/gateway/) ; sans entree, une PR qui ne
# touche que lui n'executerait AUCUNE jambe pytest (#10416 pattern, cf.
Expand Down Expand Up @@ -214,6 +220,8 @@ on:
- 'GradeBookApp/**'
# #21104 pli 0 Origami (mirror of the push path above).
- 'MyIA.AI.Notebooks/GameTheory/gateway/**'
# #21764 (mirror of the push path above).
- 'MyIA.AI.Notebooks/GameTheory/GameTheory-06-EvolutionTrust-Python.ipynb'
# #21100 pli 0 Origami (Kernel Gateway) (mirror of the push path above).
- 'MyIA.AI.Notebooks/SymbolicAI/SMT/Z3-API/gateway/**'
workflow_dispatch:
Expand Down Expand Up @@ -324,7 +332,13 @@ jobs:
# Mirror the ML job's scientific stack where suites reuse it, and add
# the lightweight deps the scripts/ suites import that the ML job omits
# (requests, bcrypt, jupyter_client — see #2871 root-cause list).
pip install numpy pandas scipy pyarrow pytest
# pyarrow >= 26 (#21764) : sous 26, le noyau ipykernel meurt
# (Unhandled SIGSEGV) au chargement de pyarrow, sur la chaine
# torch -> dask -> xarray -> pandas -> pyarrow. Mesure : 24.0.0
# crash, 26.0.0 OK ; l'image CI portait 25.0.1, non mesure. Sans
# plancher, `pip install pyarrow` est satisfait par la version
# deja presente dans l'image (25.0.1) et n'echange rien.
pip install numpy pandas scipy "pyarrow>=26" pytest
pip install requests bcrypt jupyter_client
# ipykernel (#18279) : ce job n'enregistrait AUCUN kernelspec
# `python3`, si bien que les deux tests d'integration de
Expand Down Expand Up @@ -353,6 +367,10 @@ jobs:
# (scripts/genai-stack/requirements.txt). Lets us re-include the
# genai test suites that were excluded as missing-dep (#2871 part 2).
pip install papermill nbformat
# Version RESOLUE de pyarrow (#21764) : l'acceptance demande que le
# log affiche la version installee, pas seulement la contrainte.
# Un plancher non affiche est un plancher non prouve.
python -c "import pyarrow; print('pyarrow resolu:', pyarrow.__version__)"
# nbconvert: scripts/notebook_tools/render_oversized_nbconvert.py +
# scripts/tests/test_render_oversized_nbconvert.py (#10969). The
# fail_non_notebook test asserts the real nbformat NotJSONError text,
Expand Down Expand Up @@ -404,6 +422,25 @@ jobs:
# a timeout that wants raising. See #14598 / #15574.
pip install pytest-xdist

- name: GameTheory-06 bout-en-bout sur l'image CI (#21764)
# Le carnet GameTheory-06 importe `axelrod` ; sous pyarrow < 26 le
# noyau ipykernel mourait (Unhandled SIGSEGV) au chargement de pyarrow
# (chaine torch -> dask -> xarray -> pandas -> pyarrow) : le carnet ne
# pouvait pas etre reexecute de facon fiable par la flotte, et le
# carnet GameTheory-06 de #21662 en faisait les frais. Ce step
# re-execute le carnet DANS l'image CI et verifie une seule session,
# une suite d'execution_count continue et 0 erreur -- l'acceptance
# mesuree sur l'image, pas supposee. Le plancher pyarrow>=26 est pose
# plus haut, dans le meme job.
run: |
pip install "axelrod>=4.0.0"
python -m papermill \
MyIA.AI.Notebooks/GameTheory/GameTheory-06-EvolutionTrust-Python.ipynb \
/tmp/gt06-ci.ipynb -k python3
python scripts/notebook_tools/check_exec_sequence.py /tmp/gt06-ci.ipynb --check
env:
MPLBACKEND: Agg

- name: Run tests
# Scope to the scripts/ + notebook_tools/ suites this job is named for.
# The repo-wide pytest.ini testpaths also list GameTheory/tests,
Expand Down
1 change: 1 addition & 0 deletions MyIA.AI.Notebooks/GameTheory/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ tqdm>=4.65.0

# Data handling
pandas>=2.0.0
pyarrow>=26.0.0 # kernel ipykernel SIGSEGV sous 26 (#21764) — plancher de la famille GameTheory (pyarrow>=26 exige Python>=3.11)

# Causal inference (GameTheory-04f, lésion de Fisher par modèle causal graphique)
dowhy>=0.12
Expand Down
161 changes: 161 additions & 0 deletions scripts/tests/test_pyarrow_floor_consistency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
"""Coherence des planchers `pyarrow` de la flotte (issue #21764).

Sous pyarrow 26, le noyau ipykernel meurt (`Unhandled SIGSEGV`) au chargement
de pyarrow, sur la chaine `torch -> dask -> xarray -> pandas -> pyarrow`.
Mesure : 24.0.0 crash, 26.0.0 OK ; l'image CI portait 25.0.1, non mesure.

Ce test verrouille les TROIS formes que doit prendre le plancher :

1. les deux workflows qui installent pyarrow imposent une version ** bornee** ;
2. chaque workflow **affiche la version resolue** (un plancher non affiche
n'est pas prouve) ;
3. le plancher vit dans un **fichier de dependances lisible** de la famille,
que les images d'agents peuvent reprendre -- pas seulement en ligne de
commande de workflow.

Il se valide par ses faux negatifs : une suppression du plancher, une
regression vers `pyarrow` sans borne, ou la disparition de l'affichage de la
version doivent le faire rougir.
"""

from __future__ import annotations

import re
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[2]

WORKFLOWS = [
REPO_ROOT / ".github" / "workflows" / "scripts-tests.yml",
REPO_ROOT / ".github" / "workflows" / "ml-tests.yml",
]

FAMILY_REQUIREMENTS = REPO_ROOT / "MyIA.AI.Notebooks" / "GameTheory" / "requirements.txt"

FLOOR = "pyarrow>=26"

# Une installation de pyarrow SANS borne : `pip install ... pyarrow ...`.
UNBOUNDED_RE = re.compile(r"pip\s+install[^\n]*?(?<![\w>=.-])pyarrow(?![\w>=.-])")

# L'affichage de la version RESOLUE, dans sa forme EXECUTABLE :
# `python -c "import pyarrow; print('pyarrow resolu:', pyarrow.__version__)"`.
# La commande et la mention de `pyarrow.__version__` doivent etre sur la MEME
# ligne : une ligne commentee ne repond pas a l'acceptance #1.
PRINT_VERSION_RE = re.compile(r"python\s+-c\s+[^\n]*pyarrow\.__version__")


def _without_comment_lines(text: str) -> str:
"""Le texte sans ses lignes de commentaire.

Un commentaire qui MENTIONNE `pip install pyarrow` (pour expliquer le
defaut) n'est pas une installation : le scanner ne doit pas le lire comme
tel. On ne retire donc que les lignes dont le contenu est un commentaire,
pas les commentaires de fin de ligne (ceux-ci precedent la commande).
"""
kept = []
for line in text.splitlines():
if line.strip().startswith("#"):
continue
kept.append(line)
return "\n".join(kept)


def _code_lines(path: Path) -> str:
"""Le workflow sans ses lignes de commentaire."""
return _without_comment_lines(path.read_text(encoding="utf-8"))


def test_workflows_install_a_bounded_pyarrow():
"""Les deux workflows imposent pyarrow>=26, pas `pyarrow` nu."""
for wf in WORKFLOWS:
text = _code_lines(wf)
assert FLOOR in text, f"{wf.name}: plancher {FLOOR} absent"
unbounded = UNBOUNDED_RE.findall(text)
assert not unbounded, (
f"{wf.name}: installation de pyarrow sans borne -- le plancher "
f"serait satisfait par la version deja presente dans l'image "
f"(25.0.1, non mesure) : {unbounded}"
)


def _prints_resolved_version(text: str) -> bool:
"""Le texte execute-t-il l'affichage de la version resolue ?

On ne lit que les lignes EXECUTABLES : une commande commentee n'affiche
rien dans le log. Le faux negatif (une commande passee en commentaire
laissait la suite verte) a ete mesure par la relecture tierce de #21783.
"""
return bool(PRINT_VERSION_RE.search(_without_comment_lines(text)))


def test_workflows_print_the_resolved_version():
"""Chaque workflow affiche la version RESOLUE de pyarrow (acceptance #1)."""
for wf in WORKFLOWS:
assert _prints_resolved_version(wf.read_text(encoding="utf-8")), (
f"{wf.name}: la version resolue de pyarrow n'est pas affichee par "
f"une ligne EXECUTABLE -- le log doit la porter, pas seulement la "
f"contrainte"
)


def test_a_commented_command_does_not_count_as_printing_the_version():
"""Controle negatif : une commande commentee n'affiche pas la version.

C'est le faux negatif qui a ete mesure : commenting la commande laissait
la suite verte alors que le job n'affichait plus aucune version.
"""
commented = (
' # Version RESOLUE de pyarrow (#21764) : le log doit la porter\n'
' # python -c "import pyarrow; print(pyarrow.__version__)"\n'
)
assert not _prints_resolved_version(commented), (
"une commande commentee ne doit pas satisfaire l'acceptance #1"
)
executable = ' python -c "import pyarrow; print(pyarrow.__version__)"\n'
assert _prints_resolved_version(executable), (
"la forme executable doit etre reconnue"
)


def test_floor_lives_in_a_readable_dependency_file():
"""Le plancher est dans le requirements lisible de la famille (#21764)."""
text = FAMILY_REQUIREMENTS.read_text(encoding="utf-8")
assert FLOOR in text, (
f"{FAMILY_REQUIREMENTS.name}: plancher {FLOOR} absent -- les images "
f"d'agents doivent pouvoir le reprendre sans lire un workflow"
)


def test_floor_matches_the_requirement_file():
"""Le plancher du workflow et celui du fichier de dependances coincident.

La comparaison est REELLE : les versions lues dans les workflows (lignes
executables) et celles du requirements sont mises en egale. La version
precedente ne lisait aucun workflow -- son nom promettait une comparaison
que le corps ne faisait pas (reserve non bloquante de la relecture
tierce de #21783).
"""
floor_in_file = re.search(r"pyarrow>=(\d+)", FAMILY_REQUIREMENTS.read_text(encoding="utf-8"))
assert floor_in_file is not None, "plancher illisible dans le requirements"
version = int(floor_in_file.group(1))
assert version >= 26, f"plancher pyarrow>={version} sous le seuil mesure (26)"
for wf in WORKFLOWS:
floors = {int(v) for v in re.findall(r"pyarrow>=(\d+)", _code_lines(wf))}
assert floors, f"{wf.name}: plancher illisible dans les lignes executables"
assert floors == {version}, (
f"{wf.name}: plancher workflow {sorted(floors)} != plancher "
f"requirements {version} -- les deux doivent coincider"
)


if __name__ == "__main__":
for fn in (
test_workflows_install_a_bounded_pyarrow,
test_workflows_print_the_resolved_version,
test_a_commented_command_does_not_count_as_printing_the_version,
test_floor_lives_in_a_readable_dependency_file,
test_floor_matches_the_requirement_file,
):
fn()
print(f"OK: {fn.__name__}")
print("All tests passed.")
Loading