erplibre/test/test_install_sh_probe.py
Mathieu Benoit 0f0088e7ae [FIX] install.sh: probe the version by running code, not with -V
"python -V" answers before loading the standard library. A venv whose
interpreter cannot find it, such as a checkout mounted from another machine,
still reported its version and passed as healthy. install.sh then handed TODO
to it, which died on "No module named 'encodings'", and the "venv built
elsewhere" guard never printed. The probe now runs "import sys": an
interpreter that cannot start returns nothing.
Checked: a fake python that answers -V but fails otherwise makes the test
fail on the old code and pass with the fix.

--- FR ---

[FIX] install.sh : sonder la version en exécutant du code, non par -V

« python -V » répond avant de charger la bibliothèque standard. Un venv dont
l'interpréteur ne la trouve pas, comme un checkout monté depuis une autre
machine, rendait donc sa version et passait pour sain. install.sh lui passait
alors TODO, qui mourait sur « No module named 'encodings' », et le garde
« venv bâti ailleurs » ne s'affichait jamais. La sonde exécute maintenant
« import sys » : un interpréteur qui ne démarre pas ne rend rien.
Vérifié : un faux python sain à -V mais mort ailleurs fait échouer le test
sur l'ancien code et le réussit avec la correction.

Assisted-by: Claude Opus 5.5
2026-09-25 01:29:53 -04:00

68 lines
2.2 KiB
Python

#!/usr/bin/env python3
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
"""La sonde de version d'install.sh : lire la version en exécutant du code.
« python -V » répond avant le chargement de la bibliothèque standard. Un venv
dont l'interpréteur ne trouve pas celle-ci — un checkout monté depuis une
autre machine — rend donc sa version, passe pour sain, et meurt au lancement
de TODO avec son pavé d'initialisation. La sonde doit le déclarer muet.
"""
import os
import stat
import subprocess
import tempfile
import unittest
from pathlib import Path
RACINE = Path(__file__).resolve().parent.parent
# Répond à -V comme un vrai interpréteur, échoue sur tout le reste : c'est la
# conduite d'un python qui ne trouve pas sa bibliothèque standard.
PYTHON_MORT = """#!/bin/sh
if [ "$1" = "-V" ]; then echo "Python 3.12.10"; exit 0; fi
echo "Fatal Python error: init_fs_encoding" >&2
exit 1
"""
def el_mineure(executable):
"""Rend la sortie de la fonction el_mineure d'install.sh sur l'exécutable."""
script = (
"source <(sed -n '/^el_mineure()/,/^}/p' install.sh); "
'el_mineure "$1"'
)
return subprocess.run(
["bash", "-c", script, "sonde", executable],
cwd=RACINE,
capture_output=True,
text=True,
check=False,
).stdout.strip()
class TestSondeDeVersion(unittest.TestCase):
def test_un_interpreteur_sain_rend_majeure_mineure(self):
python = subprocess.run(
["python3", "-c", "import sys; print('%d.%d' % sys.version_info[:2])"],
capture_output=True,
text=True,
check=True,
).stdout.strip()
self.assertEqual(el_mineure("python3"), python)
def test_un_interpreteur_qui_ne_demarre_pas_ne_rend_rien(self):
with tempfile.TemporaryDirectory() as dossier:
faux = os.path.join(dossier, "python")
Path(faux).write_text(PYTHON_MORT)
os.chmod(faux, os.stat(faux).st_mode | stat.S_IEXEC)
self.assertEqual(el_mineure(faux), "")
def test_un_interpreteur_absent_ne_rend_rien(self):
self.assertEqual(el_mineure("/chemin/qui/n/existe/pas"), "")
if __name__ == "__main__":
unittest.main()