docs.py
Rama main · réplica de lectura del repositorio original.
"""Tient les README au même niveau que le code, à partir de la même table.
La liste des verbes existait en trois exemplaires : l'aide en ligne, la table
de délégation, et les README. Ils ont divergé, comme toujours dans ce cas :
« autostart » manquait dans une description, « login » et « list » n'apparaissaient
nulle part dans la liste des verbes affichée.
Une seule table fait désormais foi, `runtime.VERBES`. Ce programme en recopie le
contenu entre deux balises dans chaque README.
python docs.py met les README à jour
python docs.py --check échoue s'ils ne sont plus à jour
Le mode --check tourne en intégration continue : une dérive doit être signalée,
pas tolérée jusqu'à ce que quelqu'un s'en aperçoive.
"""
import argparse
from pathlib import Path
import runtime
BASE_DIR = Path(__file__).resolve().parent
DEBUT = "<!-- verbes:début -->"
FIN = "<!-- verbes:fin -->"
def bloc(langue: str) -> str:
"""Liste des verbes, en bloc de code, dans la langue demandée."""
largeur = max(len(v) for v in runtime.VERBES)
lignes = [f"{nom:<{largeur}} # {getattr(verbe, langue)}"
for nom, verbe in runtime.VERBES.items()]
entete = ("Une commande, un verbe par action. `blink2video <verbe> --help` donne "
"les options de chacun."
if langue == "fr" else
"One command, one verb per action. `blink2video <verb> --help` gives "
"each one's options.")
corps = "\n".join(f"blink2video {ligne}" for ligne in lignes)
return f"{DEBUT}\n{entete}\n\n```bash\n{corps}\n```\n{FIN}"
def caracteres_parasites(texte: str) -> list:
"""Lignes contenant un caractère de contrôle.
Un correctif appliqué par script a un jour transformé « C:\\path\\to » en tabulation et « \\blink » en retour arrière, au
beau milieu d'un exemple PowerShell. Invisible à la relecture, recopié tel
quel par le lecteur, et évidemment inopérant."""
return [
(numero, ligne)
for numero, ligne in enumerate(texte.splitlines(), 1)
if any(ord(caractere) < 32 for caractere in ligne)
]
def appliquer(verifier: bool) -> int:
ecarts = []
for chemin, langue in (("README.md", "en"), ("README.fr.md", "fr")):
fichier = BASE_DIR / chemin
texte = fichier.read_text(encoding="utf-8")
for numero, ligne in caracteres_parasites(texte):
print(f"{chemin}:{numero} : caractère de contrôle dans « {ligne.strip()} »")
ecarts.append(chemin)
if DEBUT not in texte or FIN not in texte:
print(f"{chemin} : balises {DEBUT} … {FIN} absentes")
ecarts.append(chemin)
continue
avant, reste = texte.split(DEBUT, 1)
_, apres = reste.split(FIN, 1)
attendu = avant + bloc(langue) + apres
if attendu == texte:
print(f"{chemin} : à jour")
continue
ecarts.append(chemin)
if verifier:
print(f"{chemin} : PÉRIMÉ, lancez « python docs.py »")
else:
fichier.write_text(attendu, encoding="utf-8")
print(f"{chemin} : mis à jour")
if verifier and ecarts:
return 1
return 0
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--check", action="store_true",
help="échouer si les README ne sont pas à jour, sans les modifier")
args = parser.parse_args()
return appliquer(args.check)
if __name__ == "__main__":
raise SystemExit(main())