[FIX] démarrage : choisir un interpréteur capable de lire TODO

install.sh et make lançaient todo.py par son hashbang, donc par le python3 du
PATH, sans rien garantir de sa version : une syntaxe plus récente l'arrête en
SyntaxError au chargement, et le poste hors d'âge perd tout moyen de lancer
l'installation qui l'en sortirait. L'ordre est désormais le venv d'outillage,
sinon le python3 du PATH s'il suffit, sinon l'installation. TODO lui-même se
relance dans le venv, vérifie version et modules avant ses imports, et propose
l'installation en terminal — ce que « make » sur un clone neuf promet depuis
toujours sans le tenir.
Vérifié : venv à jour, venv périmé, systèmes en 3.10, 3.14 et 3.15 vont chacun
où il faut ; un import du module ne relance rien et lève comme avant.

--- EN ---

install.sh and make ran todo.py through its hashbang, so through the python3 of
the PATH, with no guarantee about its version: a newer syntax stops it with a
SyntaxError at load, and the ageing machine loses every way to start the
install that would rescue it. The order is now the tools venv, else the python3
of the PATH when it suffices, else the install. TODO itself relaunches in the
venv, checks version and modules before its imports, and offers the install in
a terminal — what "make" on a fresh clone has always promised without
keeping.
Checked: fresh venv, stale venv, systems on 3.10, 3.14 and 3.15 each go where
they should; importing the module relaunches nothing and raises as before.

Assisted-by: Claude Opus 5
This commit is contained in:
Mathieu Benoit 2026-09-24 13:32:16 -04:00
parent 42e10bcced
commit 7a10f268df
10 changed files with 226 additions and 2 deletions

View file

@ -56,9 +56,14 @@ est HORS SERVICE : une simple différence de version le laisse en place, parce
que le rebâtir refait une installation Poetry entière. Un répertoire sans que le rebâtir refait une installation Poetry entière. Un répertoire sans
`pyvenv.cfg` n'est jamais effacé. `pyvenv.cfg` n'est jamais effacé.
`make` / `make todo` hors venv : TODO se relance dans `.venv.erplibre` ; si le
venv manque, il propose `install_erplibre.sh` en terminal, ou nomme la
commande.
Le hook `pre-commit` relaie `script/analyse/check_python_version.py` : il Le hook `pre-commit` relaie `script/analyse/check_python_version.py` : il
signale le source qui ne parse pas sous cette version, sans bloquer, et dit signale le source qui ne parse pas sous cette version, sans bloquer, et dit
quand aucun interpréteur de cette version n'était là pour vérifier. quand aucun interpréteur de cette version n'était là pour vérifier.
L'image Docker de production bâtit `.venv.erplibre` sur le Python d'Odoo de son L'image Docker de production bâtit `.venv.erplibre` sur le Python d'Odoo de son
image de base, et s'arrête si ce Python ne sait pas lire `script/`. image de base, et s'arrête si ce Python ne sait pas lire `script/`.

View file

@ -158,6 +158,7 @@ au [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- `--bios` is refused on an image with no BIOS boot sector, and says why. Forced there, it gave a VM reported « running » with a silent console — the very failure that flag exists to avoid elsewhere - `--bios` is refused on an image with no BIOS boot sector, and says why. Forced there, it gave a VM reported « running » with a silent console — the very failure that flag exists to avoid elsewhere
- The tooling virtual environment `.venv.erplibre` runs Python 3.14.7, independently of the Odoo one (3.12.10 for Odoo 18.0). `install_erplibre.sh` builds it through `install_venv.sh` and `EL_PYTHON_PROVIDER` instead of the system `python3` - The tooling virtual environment `.venv.erplibre` runs Python 3.14.7, independently of the Odoo one (3.12.10 for Odoo 18.0). `install_erplibre.sh` builds it through `install_venv.sh` and `EL_PYTHON_PROVIDER` instead of the system `python3`
- `.venv.erplibre` on an incompatible Python is DELETED and rebuilt; whatever was installed in it by hand goes with it. Odoo's venv is kept when it merely differs in version, and rebuilt only when it is unusable: rebuilding it redoes a whole Poetry install. A directory without `pyvenv.cfg` is never deleted - `.venv.erplibre` on an incompatible Python is DELETED and rebuilt; whatever was installed in it by hand goes with it. Odoo's venv is kept when it merely differs in version, and rebuilt only when it is unusable: rebuilding it redoes a whole Poetry install. A directory without `pyvenv.cfg` is never deleted
- `make` and `make todo` go through `install.sh`, which picks an interpreter able to READ the code before running it: `.venv.erplibre` on the right version, else a recent enough system `python3`, else the install. A system older than `conf/python-erplibre-version` would otherwise stop on a syntax error raised before any guard could name the command to type. TODO then relaunches itself in `.venv.erplibre`, or offers to run `install_erplibre.sh` in a terminal
- The production Docker image builds `.venv.erplibre` on Odoo's Python and stops when that Python cannot parse `script/` - The production Docker image builds `.venv.erplibre` on Odoo's Python and stops when that Python cannot parse `script/`
- The PATCH bounds only Odoo's venv, whose pyproject requires `>=3.12.10,<3.13`. For the tooling one, the major.minor is enough: requiring the patch turned away a distribution's Python one step behind — NixOS 25.11 ships 3.14.2 where conf asks 3.14.7 — and made pyenv COMPILE CPython for a difference nothing needs - The PATCH bounds only Odoo's venv, whose pyproject requires `>=3.12.10,<3.13`. For the tooling one, the major.minor is enough: requiring the patch turned away a distribution's Python one step behind — NixOS 25.11 ships 3.14.2 where conf asks 3.14.7 — and made pyenv COMPILE CPython for a difference nothing needs
- `make format` picks the formatter from each file's context: an Odoo module keeps isort and black on `py37`, the series still supported going down that far, while this repository's own tooling goes through ruff, configured once in `.ruff.toml`. ruff follows CPython's versions, where black 24.8.0 stops at `py313`, and its import sorting replaces isort; it is also what the OCA standard uses since it left black - `make format` picks the formatter from each file's context: an Odoo module keeps isort and black on `py37`, the series still supported going down that far, while this repository's own tooling goes through ruff, configured once in `.ruff.toml`. ruff follows CPython's versions, where black 24.8.0 stops at `py313`, and its import sorting replaces isort; it is also what the OCA standard uses since it left black
@ -180,6 +181,7 @@ au [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- `--bios` est refusé sur une image sans secteur d'amorçage BIOS, et dit pourquoi. Forcé là, il donnait une VM « running » à console muette — la panne même que ce drapeau évite ailleurs - `--bios` est refusé sur une image sans secteur d'amorçage BIOS, et dit pourquoi. Forcé là, il donnait une VM « running » à console muette — la panne même que ce drapeau évite ailleurs
- L'environnement virtuel d'outillage `.venv.erplibre` tourne en Python 3.14.7, indépendamment de celui d'Odoo (3.12.10 pour Odoo 18.0). `install_erplibre.sh` le bâtit par `install_venv.sh` et `EL_PYTHON_PROVIDER` plutôt qu'avec le `python3` du système - L'environnement virtuel d'outillage `.venv.erplibre` tourne en Python 3.14.7, indépendamment de celui d'Odoo (3.12.10 pour Odoo 18.0). `install_erplibre.sh` le bâtit par `install_venv.sh` et `EL_PYTHON_PROVIDER` plutôt qu'avec le `python3` du système
- `.venv.erplibre` sur un Python incompatible est SUPPRIMÉ puis rebâti ; ce qu'on y avait posé à la main part avec lui. Le venv d'Odoo est conservé quand seule sa version diffère, et rebâti seulement s'il est hors service : le rebâtir refait une installation Poetry entière. Un répertoire sans `pyvenv.cfg` n'est jamais effacé - `.venv.erplibre` sur un Python incompatible est SUPPRIMÉ puis rebâti ; ce qu'on y avait posé à la main part avec lui. Le venv d'Odoo est conservé quand seule sa version diffère, et rebâti seulement s'il est hors service : le rebâtir refait une installation Poetry entière. Un répertoire sans `pyvenv.cfg` n'est jamais effacé
- `make` et `make todo` passent par `install.sh`, qui choisit un interpréteur capable de LIRE le code avant de le lancer : `.venv.erplibre` s'il porte la bonne version, sinon le `python3` du système s'il est assez récent, sinon l'installation. Un système plus ancien que `conf/python-erplibre-version` s'arrêterait autrement sur une erreur de syntaxe levée avant qu'aucun garde puisse nommer la commande à taper. TODO se relance ensuite dans `.venv.erplibre`, ou propose de lancer `install_erplibre.sh` en terminal
- L'image Docker de production bâtit `.venv.erplibre` sur le Python d'Odoo et s'arrête quand ce Python ne sait pas lire `script/` - L'image Docker de production bâtit `.venv.erplibre` sur le Python d'Odoo et s'arrête quand ce Python ne sait pas lire `script/`
- Le PATCH ne borne que le venv d'Odoo, dont le pyproject exige `>=3.12.10,<3.13`. Pour celui de l'outillage, la majeure.mineure suffit : exiger le patch écartait le Python d'une distribution d'un cran en retard — NixOS 25.11 livre 3.14.2 quand conf demande 3.14.7 — et faisait COMPILER CPython à pyenv pour une différence que rien ne réclame - Le PATCH ne borne que le venv d'Odoo, dont le pyproject exige `>=3.12.10,<3.13`. Pour celui de l'outillage, la majeure.mineure suffit : exiger le patch écartait le Python d'une distribution d'un cran en retard — NixOS 25.11 livre 3.14.2 quand conf demande 3.14.7 — et faisait COMPILER CPython à pyenv pour une différence que rien ne réclame
- `make format` choisit le formateur d'après le contexte de chaque fichier : un module Odoo garde isort et black en `py37`, la série la plus ancienne encore supportée, quand l'outillage de ce dépôt passe par ruff, réglé une fois dans `.ruff.toml`. ruff suit les versions de CPython, là où black 24.8.0 s'arrête à `py313`, et son tri d'imports remplace isort ; c'est aussi ce qu'emploie la norme OCA depuis qu'elle a quitté black - `make format` choisit le formateur d'après le contexte de chaque fichier : un module Odoo garde isort et black en `py37`, la série la plus ancienne encore supportée, quand l'outillage de ce dépôt passe par ruff, réglé une fois dans `.ruff.toml`. ruff suit les versions de CPython, là où black 24.8.0 s'arrête à `py313`, et son tri d'imports remplace isort ; c'est aussi ce qu'emploie la norme OCA depuis qu'elle a quitté black

View file

@ -80,6 +80,7 @@ au [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- `--bios` est refusé sur une image sans secteur d'amorçage BIOS, et dit pourquoi. Forcé là, il donnait une VM « running » à console muette — la panne même que ce drapeau évite ailleurs - `--bios` est refusé sur une image sans secteur d'amorçage BIOS, et dit pourquoi. Forcé là, il donnait une VM « running » à console muette — la panne même que ce drapeau évite ailleurs
- L'environnement virtuel d'outillage `.venv.erplibre` tourne en Python 3.14.7, indépendamment de celui d'Odoo (3.12.10 pour Odoo 18.0). `install_erplibre.sh` le bâtit par `install_venv.sh` et `EL_PYTHON_PROVIDER` plutôt qu'avec le `python3` du système - L'environnement virtuel d'outillage `.venv.erplibre` tourne en Python 3.14.7, indépendamment de celui d'Odoo (3.12.10 pour Odoo 18.0). `install_erplibre.sh` le bâtit par `install_venv.sh` et `EL_PYTHON_PROVIDER` plutôt qu'avec le `python3` du système
- `.venv.erplibre` sur un Python incompatible est SUPPRIMÉ puis rebâti ; ce qu'on y avait posé à la main part avec lui. Le venv d'Odoo est conservé quand seule sa version diffère, et rebâti seulement s'il est hors service : le rebâtir refait une installation Poetry entière. Un répertoire sans `pyvenv.cfg` n'est jamais effacé - `.venv.erplibre` sur un Python incompatible est SUPPRIMÉ puis rebâti ; ce qu'on y avait posé à la main part avec lui. Le venv d'Odoo est conservé quand seule sa version diffère, et rebâti seulement s'il est hors service : le rebâtir refait une installation Poetry entière. Un répertoire sans `pyvenv.cfg` n'est jamais effacé
- `make` et `make todo` passent par `install.sh`, qui choisit un interpréteur capable de LIRE le code avant de le lancer : `.venv.erplibre` s'il porte la bonne version, sinon le `python3` du système s'il est assez récent, sinon l'installation. Un système plus ancien que `conf/python-erplibre-version` s'arrêterait autrement sur une erreur de syntaxe levée avant qu'aucun garde puisse nommer la commande à taper. TODO se relance ensuite dans `.venv.erplibre`, ou propose de lancer `install_erplibre.sh` en terminal
- L'image Docker de production bâtit `.venv.erplibre` sur le Python d'Odoo et s'arrête quand ce Python ne sait pas lire `script/` - L'image Docker de production bâtit `.venv.erplibre` sur le Python d'Odoo et s'arrête quand ce Python ne sait pas lire `script/`
- Le PATCH ne borne que le venv d'Odoo, dont le pyproject exige `>=3.12.10,<3.13`. Pour celui de l'outillage, la majeure.mineure suffit : exiger le patch écartait le Python d'une distribution d'un cran en retard — NixOS 25.11 livre 3.14.2 quand conf demande 3.14.7 — et faisait COMPILER CPython à pyenv pour une différence que rien ne réclame - Le PATCH ne borne que le venv d'Odoo, dont le pyproject exige `>=3.12.10,<3.13`. Pour celui de l'outillage, la majeure.mineure suffit : exiger le patch écartait le Python d'une distribution d'un cran en retard — NixOS 25.11 livre 3.14.2 quand conf demande 3.14.7 — et faisait COMPILER CPython à pyenv pour une différence que rien ne réclame
- `make format` choisit le formateur d'après le contexte de chaque fichier : un module Odoo garde isort et black en `py37`, la série la plus ancienne encore supportée, quand l'outillage de ce dépôt passe par ruff, réglé une fois dans `.ruff.toml`. ruff suit les versions de CPython, là où black 24.8.0 s'arrête à `py313`, et son tri d'imports remplace isort ; c'est aussi ce qu'emploie la norme OCA depuis qu'elle a quitté black - `make format` choisit le formateur d'après le contexte de chaque fichier : un module Odoo garde isort et black en `py37`, la série la plus ancienne encore supportée, quand l'outillage de ce dépôt passe par ruff, réglé une fois dans `.ruff.toml`. ruff suit les versions de CPython, là où black 24.8.0 s'arrête à `py313`, et son tri d'imports remplace isort ; c'est aussi ce qu'emploie la norme OCA depuis qu'elle a quitté black

View file

@ -80,6 +80,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- `--bios` is refused on an image with no BIOS boot sector, and says why. Forced there, it gave a VM reported « running » with a silent console — the very failure that flag exists to avoid elsewhere - `--bios` is refused on an image with no BIOS boot sector, and says why. Forced there, it gave a VM reported « running » with a silent console — the very failure that flag exists to avoid elsewhere
- The tooling virtual environment `.venv.erplibre` runs Python 3.14.7, independently of the Odoo one (3.12.10 for Odoo 18.0). `install_erplibre.sh` builds it through `install_venv.sh` and `EL_PYTHON_PROVIDER` instead of the system `python3` - The tooling virtual environment `.venv.erplibre` runs Python 3.14.7, independently of the Odoo one (3.12.10 for Odoo 18.0). `install_erplibre.sh` builds it through `install_venv.sh` and `EL_PYTHON_PROVIDER` instead of the system `python3`
- `.venv.erplibre` on an incompatible Python is DELETED and rebuilt; whatever was installed in it by hand goes with it. Odoo's venv is kept when it merely differs in version, and rebuilt only when it is unusable: rebuilding it redoes a whole Poetry install. A directory without `pyvenv.cfg` is never deleted - `.venv.erplibre` on an incompatible Python is DELETED and rebuilt; whatever was installed in it by hand goes with it. Odoo's venv is kept when it merely differs in version, and rebuilt only when it is unusable: rebuilding it redoes a whole Poetry install. A directory without `pyvenv.cfg` is never deleted
- `make` and `make todo` go through `install.sh`, which picks an interpreter able to READ the code before running it: `.venv.erplibre` on the right version, else a recent enough system `python3`, else the install. A system older than `conf/python-erplibre-version` would otherwise stop on a syntax error raised before any guard could name the command to type. TODO then relaunches itself in `.venv.erplibre`, or offers to run `install_erplibre.sh` in a terminal
- The production Docker image builds `.venv.erplibre` on Odoo's Python and stops when that Python cannot parse `script/` - The production Docker image builds `.venv.erplibre` on Odoo's Python and stops when that Python cannot parse `script/`
- The PATCH bounds only Odoo's venv, whose pyproject requires `>=3.12.10,<3.13`. For the tooling one, the major.minor is enough: requiring the patch turned away a distribution's Python one step behind — NixOS 25.11 ships 3.14.2 where conf asks 3.14.7 — and made pyenv COMPILE CPython for a difference nothing needs - The PATCH bounds only Odoo's venv, whose pyproject requires `>=3.12.10,<3.13`. For the tooling one, the major.minor is enough: requiring the patch turned away a distribution's Python one step behind — NixOS 25.11 ships 3.14.2 where conf asks 3.14.7 — and made pyenv COMPILE CPython for a difference nothing needs
- `make format` picks the formatter from each file's context: an Odoo module keeps isort and black on `py37`, the series still supported going down that far, while this repository's own tooling goes through ruff, configured once in `.ruff.toml`. ruff follows CPython's versions, where black 24.8.0 stops at `py313`, and its import sorting replaces isort; it is also what the OCA standard uses since it left black - `make format` picks the formatter from each file's context: an Odoo module keeps isort and black on `py37`, the series still supported going down that far, while this repository's own tooling goes through ruff, configured once in `.ruff.toml`. ruff follows CPython's versions, where black 24.8.0 stops at `py313`, and its import sorting replaces isort; it is also what the OCA standard uses since it left black

View file

@ -254,6 +254,33 @@ make
<!-- [en] --> <!-- [en] -->
`make` and `./install.sh` start TODO through the interpreter that can read it:
`.venv.erplibre` when it carries the right version, otherwise the system
`python3` when it is recent enough, otherwise the install itself — a system
older than `conf/python-erplibre-version` cannot parse the code, so it goes
straight to the install rather than stopping on a syntax error. When the
environment is missing but the system Python suffices, TODO offers to run
`./script/install/install_erplibre.sh` (in a terminal) or prints that command. The install builds the environment
through `EL_PYTHON_PROVIDER` (mise or pyenv); an existing `.venv.erplibre` on
another Python version is DELETED and rebuilt, and whatever was installed in it
by hand goes with it.
<!-- [fr] -->
`make` et `./install.sh` lancent TODO par l'interpréteur capable de le lire :
`.venv.erplibre` quand il porte la bonne version, sinon le `python3` du système
s'il est assez récent, sinon l'installation elle-même — un système plus ancien
que `conf/python-erplibre-version` ne sait pas analyser le code, donc on va
droit à l'installation plutôt que de s'arrêter sur une erreur de syntaxe. Quand
l'environnement manque mais que le Python du système suffit, TODO propose de
lancer `./script/install/install_erplibre.sh` (dans un terminal) ou affiche
cette commande. L'installation bâtit l'environnement par
`EL_PYTHON_PROVIDER` (mise ou pyenv) ; un `.venv.erplibre` existant sur une
autre version de Python est SUPPRIMÉ puis rebâti, et ce qu'on y avait posé à la
main part avec lui.
<!-- [en] -->
### Manually ### Manually
Into Ubuntu, minimal dependency: Into Ubuntu, minimal dependency:

View file

@ -126,6 +126,19 @@ make
``` ```
`make` et `./install.sh` lancent TODO par l'interpréteur capable de le lire :
`.venv.erplibre` quand il porte la bonne version, sinon le `python3` du système
s'il est assez récent, sinon l'installation elle-même — un système plus ancien
que `conf/python-erplibre-version` ne sait pas analyser le code, donc on va
droit à l'installation plutôt que de s'arrêter sur une erreur de syntaxe. Quand
l'environnement manque mais que le Python du système suffit, TODO propose de
lancer `./script/install/install_erplibre.sh` (dans un terminal) ou affiche
cette commande. L'installation bâtit l'environnement par
`EL_PYTHON_PROVIDER` (mise ou pyenv) ; un `.venv.erplibre` existant sur une
autre version de Python est SUPPRIMÉ puis rebâti, et ce qu'on y avait posé à la
main part avec lui.
### Manuellement ### Manuellement
Sous Ubuntu, dépendance minimale : Sous Ubuntu, dépendance minimale :

View file

@ -124,6 +124,18 @@ make
``` ```
`make` and `./install.sh` start TODO through the interpreter that can read it:
`.venv.erplibre` when it carries the right version, otherwise the system
`python3` when it is recent enough, otherwise the install itself — a system
older than `conf/python-erplibre-version` cannot parse the code, so it goes
straight to the install rather than stopping on a syntax error. When the
environment is missing but the system Python suffices, TODO offers to run
`./script/install/install_erplibre.sh` (in a terminal) or prints that command. The install builds the environment
through `EL_PYTHON_PROVIDER` (mise or pyenv); an existing `.venv.erplibre` on
another Python version is DELETED and rebuilt, and whatever was installed in it
by hand goes with it.
### Manually ### Manually
Into Ubuntu, minimal dependency: Into Ubuntu, minimal dependency:

View file

@ -2,6 +2,8 @@
# TODO # # TODO #
######## ########
# Par install.sh, et non todo.py directement : lui seul choisit un interpréteur
# capable de LIRE le code avant de le lancer, et pose le venv s'il manque.
.PHONY: todo .PHONY: todo
todo: todo:
./script/todo/todo.py ./install.sh

View file

@ -1,3 +1,69 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
#
# Point d'entrée de TODO : choisir un interpréteur capable de LIRE le code
# avant de le lui donner.
#
# todo.py porte le hashbang « python3 », donc le Python du système. Celui-ci
# est libre d'être plus vieux que conf/python-erplibre-version, qui est la
# seule version que le dépôt vise : une syntaxe qu'il ne connaît pas l'arrête
# en SyntaxError au chargement, AVANT le garde de todo.py qui aurait su nommer
# le geste. Un système hors d'âge n'a alors plus aucun moyen de lancer
# l'installation qui le tirerait de là.
#
# D'où l'ordre : le venv d'outillage, qui porte la bonne version ; sinon le
# Python du système s'il est assez récent pour lire le code, et le garde de
# todo.py prend le relais ; sinon l'installation, seule issue.
./script/todo/todo.py cd "$(dirname "$0")" || exit 1
VENV="$(xargs < conf/python-erplibre-venv 2> /dev/null)"
VOULUE="$(xargs < conf/python-erplibre-version 2> /dev/null)"
ATTENDUE="${VOULUE%.*}"
# Majeure.mineure de l'exécutable : elle seule décide de la grammaire acceptée.
# Rien n'est rendu quand la sortie n'a pas la forme « Python X.Y… » : un
# interpréteur en panne écrit son diagnostic, et le premier mot venu passerait
# sinon pour un numéro de version que « sort -V » jugerait assez récent.
el_mineure() {
"$1" -V 2>&1 | awk '$1 == "Python" {
split($2, v, ".")
if (v[1] ~ /^[0-9]+$/ && v[2] ~ /^[0-9]+$/) print v[1] "." v[2]
}'
}
# Vrai si la version lue atteint au moins celle attendue, comparée en version
# et non en chaîne : « 3.9 » est une chaîne plus grande que « 3.14 ».
el_assez_recent() {
[ -n "$1" ] && [ -n "${ATTENDUE}" ] \
&& [ "$(printf '%s\n%s\n' "${ATTENDUE}" "$1" | sort -V | head -1)" \
= "${ATTENDUE}" ]
}
PYTHON_VENV="./${VENV}/bin/python"
if [ -n "${VENV}" ] && [ -x "${PYTHON_VENV}" ] \
&& el_assez_recent "$(el_mineure "${PYTHON_VENV}")"; then
exec "${PYTHON_VENV}" ./script/todo/todo.py "$@"
fi
# Un venv dont l'interpréteur ne rend pas sa version ne démarrera pas
# davantage TODO : c'est le cas d'un checkout monté depuis une autre machine,
# dont le venv cherche sa bibliothèque standard là où elle n'est pas. Le dire,
# et couper la relance de todo.py, qui sinon se remplacerait par ce python mort
# et rendrait son pavé d'initialisation au lieu d'un message.
if [ -x "${PYTHON_VENV}" ] && [ -z "$(el_mineure "${PYTHON_VENV}")" ]; then
echo "${PYTHON_VENV} ne demarre pas : ce venv a ete bati ailleurs."
echo " Rebatissez-le ici : ./script/install/install_erplibre.sh"
export EL_TODO_VENV_RELAUNCHED=1
fi
if command -v python3 > /dev/null 2>&1 \
&& el_assez_recent "$(el_mineure python3)"; then
exec ./script/todo/todo.py "$@"
fi
echo "Python ${VOULUE:-du depot} est requis pour lire le code de TODO ;"
echo " ce systeme livre $(el_mineure python3 2> /dev/null || echo 'aucun python3')."
echo " Installation du venv d'outillage, qui le pose :"
exec ./script/todo/source_todo.sh "$@"

View file

@ -5,6 +5,7 @@
import ast import ast
import configparser import configparser
import datetime import datetime
import importlib.util
import inspect import inspect
import json import json
import logging import logging
@ -24,6 +25,98 @@ new_path = os.path.normpath(
) )
sys.path.append(new_path) sys.path.append(new_path)
# Garde d'amorçage, joué seulement quand todo.py est le programme lancé : il
# relance dans le venv d'outillage, ou refuse avec le geste qui le bâtit, au
# lieu d'une trace brute à l'import. Il parse sous 3.7 pour parler à un vieux
# python. Messages en anglais hors t() : todo_i18n n'est pas encore importé.
RELAUNCH_ENV = "EL_TODO_VENV_RELAUNCHED"
# Modules tiers que charge l'import de ce fichier et de ses mixins : un seul
# absent fait lever l'import, ou boucler crash_diagnostic. Ceux qu'un menu
# n'importe qu'à l'usage n'y figurent pas, ils ne bloquent pas le démarrage.
REQUIRED_MODULES = (
"click colorama dotenv humanize openai pykeepass urwid".split()
)
INSTALL_CMD = "./script/install/install_erplibre.sh"
def _read_conf(name, fallback):
"""Rend la première ligne non vide et non commentée de conf/<name>."""
try:
with open(os.path.join(new_path, "conf", name), encoding="utf-8") as f:
for line in f:
line = line.strip()
if line and not line.startswith("#"):
return line
except (OSError, UnicodeDecodeError):
pass
sys.stderr.write("conf/%s unreadable, assuming %s\n" % (name, fallback))
return fallback
VENV_ERPLIBRE = _read_conf("python-erplibre-venv", ".venv.erplibre")
VENV_DIR = os.path.join(new_path, VENV_ERPLIBRE)
VENV_PYTHON = os.path.join(VENV_DIR, "bin", "python")
def _in_venv():
# sys.prefix et non le binaire : <venv>/bin/python est un lien vers sa base.
return os.path.realpath(sys.prefix) == os.path.realpath(VENV_DIR)
def _relaunch(marker):
"""Remplace le processus par le python du venv ; OSError si impossible."""
env = dict(os.environ)
env[RELAUNCH_ENV] = marker
argv = [VENV_PYTHON, os.path.abspath(__file__)] + sys.argv[1:]
os.execve(VENV_PYTHON, argv, env)
def _bootstrap():
# La relance passe avant la version : un python trop vieux avec un venv
# sain doit relancer, pas refuser.
relaunched = os.environ.get(RELAUNCH_ENV)
if not relaunched and not _in_venv() and os.access(VENV_PYTHON, os.X_OK):
try:
_relaunch("1")
except OSError:
pass
voulue = _read_conf("python-erplibre-version", "3.14")
missing = [m for m in REQUIRED_MODULES if not importlib.util.find_spec(m)]
if sys.version_info[:2] >= tuple(map(int, voulue.split(".")[:2])):
if not missing:
os.environ.pop(RELAUNCH_ENV, None)
return
reason = "missing modules: %s" % ", ".join(missing)
else:
reason = "Python %s is older than %s" % (
sys.version.split()[0],
voulue,
)
print("TODO cannot start from %s: %s." % (sys.executable, reason))
print("Install the tools virtualenv %s with:" % VENV_ERPLIBRE)
print("\n %s\n" % INSTALL_CMD)
# Amorçage de « make » sur un clone neuf : proposer l'installation, une fois.
if relaunched != "installed" and os.isatty(0) and os.isatty(1):
try:
answer = input("Run it now? [y/o/N] ")
except (EOFError, KeyboardInterrupt):
answer = ""
if answer.strip().lower() in ("y", "yes", "o", "oui"):
if subprocess.call([INSTALL_CMD], cwd=new_path) != 0:
print("Installation failed, see above.")
else:
try:
_relaunch("installed")
except OSError:
print("Installed, but %s does not start." % VENV_PYTHON)
sys.exit(1)
if __name__ == "__main__":
_bootstrap()
from script.config import config_file from script.config import config_file
from script.execute import execute from script.execute import execute
from script.todo import dev_tools, ssh_config, todo_install, todo_prefs from script.todo import dev_tools, ssh_config, todo_install, todo_prefs
@ -5601,6 +5694,8 @@ class TODO(
from pykeepass import PyKeePass from pykeepass import PyKeePass
except ImportError: except ImportError:
print("Rerun and exit") print("Rerun and exit")
if _in_venv(): # la relance tournerait dans ce même venv
sys.exit(1)
self.execute.exec_command_live(cmd, source_erplibre=True) self.execute.exec_command_live(cmd, source_erplibre=True)
sys.exit(1) sys.exit(1)
print("No error") print("No error")