Programmation lettrée et Tangling avec Org Babel
Org Babel est une implémentation du paradigme de programmation lettrée de Donald Knuth. Au lieu d'écrire du code avec des commentaires épars, vous rédigez une prose centrée sur l'humain, des notes d'architecture et des justifications de conception, en intégrant les blocs de code fonctionnels à l'intérieur.
Un seul fichier Org sert de source de vérité définitive pour deux sorties distinctes :
- Weaving (Documentation Humaine) : Exporter le notebook vers du HTML formaté, du LaTeX/PDF ou du Markdown pour les lecteurs.
- Tangling (Exécution Machine) : Extraire des fichiers de code source purs et exécutables (
.py,.sh,.c,.conf,.el) pour les compilateurs, les interpréteurs et les systèmes de déploiement.
1. Bases du Tangling : L'argument d'en-tête :tangle
Par défaut, les blocs de code ne sont pas extraits. La famille d'arguments d'en-tête :tangle spécifie où et comment les blocs sont extraits sur le disque.
1.1 Les Valeurs de l'Argument :tangle
| Valeur | Comportement |
|---|---|
:tangle no (Défaut) | Le bloc n'est écrit dans aucun fichier. (Il peut néanmoins être inclus à l'intérieur d'autres blocs via <<noweb>>). |
:tangle "chemin/vers/fichier.py" | Écrit le corps du bloc directement dans le chemin de fichier spécifié (relatif au répertoire du tampon ou absolu). |
:tangle yes | Extrait vers un fichier correspondant au nom de base du fichier Org avec l'extension de fichier standard du langage (ex : model.org -> model.py). |
1.2 Arguments d'en-tête de Tangling Supplémentaires
:shebang "#!/usr/bin/env python3": Ajoute la chaîne shebang spécifiée comme première ligne du fichier extrait.:tangle-mode (identity #o755): Définit les permissions du système de fichiers sur le fichier généré (ex :#o755rend un script exécutable).:no-expand: Empêche les affectations de variables (:var) d'être intégrées dans le code extrait ; écrit uniquement le corps brut du bloc.:mkdirp yes: Crée automatiquement tous les répertoires parents inexistants dans le chemin d'extraction.
2. Portée du Tangling : Niveaux Bloc, Sous-arbre et Tampon
Les arguments de tangling peuvent être appliqués proprement à l'ensemble d'un projet :
2.1 Tangling au Niveau du Sous-Arbre (Modèle Recommandé)
Différentes rubriques peuvent extraire du code vers différents fichiers ou modules :
* Utilitaires Principaux
:PROPERTIES:
:header-args:python: :tangle src/utils.py :mkdirp yes
:END:
#+BEGIN_SRC python
def slugify(text: str) -> str:
return "-".join(text.lower().split())
#+END_SRC
* Pipeline de Traitement de Données
:PROPERTIES:
:header-args:python: :tangle src/pipeline.py :mkdirp yes
:END:
#+BEGIN_SRC python
from utils import slugify
def process_record(rec: dict) -> dict:
rec["slug"] = slugify(rec["name"])
return rec
#+END_SRC
2.2 Valeur par Défaut du Tangling pour l'Ensemble du Tampon
Définissez-le en haut du fichier :
#+PROPERTY: header-args:python :tangle src/main.py
3. Syntaxe Noweb : Assemblage Modulaire de Code
Noweb vous permet d'assembler des fichiers source plus volumineux à partir de fragments plus petits, modulaires et facilement explicables. Vous référencez un bloc de code à l'intérieur d'un autre en utilisant <<nom_du_bloc>>.
3.1 Activation de l'Expansion Noweb
Noweb est désactivé par défaut. Contrôlez-le avec l'argument d'en-tête :noweb :
Valeur :noweb | Évalué dans Emacs ? | Extrait sur le Disque ? | Exporté dans le Document ? |
|---|---|---|---|
no (Défaut) | Non | Non | Non |
yes | Oui | Oui | Oui |
tangle | Non | Oui | Non |
strip-tangle | Oui | Non (références supprimées) | Oui |
no-export | Oui | Oui | Non |
eval | Oui | Non | Non |
3.2 Exemple Noweb : Construction Littérale d'un Script
#+NAME: imports
#+BEGIN_SRC python :exports none
import sys
import argparse
from pathlib import Path
#+END_SRC
#+NAME: analyseur-arguments
#+BEGIN_SRC python :exports code
parser = argparse.ArgumentParser(description="Traiter le jeu de données.")
parser.add_argument("--input", required=True, type=Path, help="Chemin du fichier CSV d'entrée")
parser.add_argument("--threshold", default=0.5, type=float, help="Seuil de score")
args = parser.parse_args()
#+END_SRC
#+NAME: traiter-donnees
#+BEGIN_SRC python :exports code
print(f"Chargement des données depuis {args.input}...")
# Logique de traitement ici
#+END_SRC
# Assemblage du script exécutable final :
#+NAME: main-cli
#+BEGIN_SRC python :tangle bin/run_pipeline.py :shebang "#!/usr/bin/env python3" :tangle-mode (identity #o755) :noweb yes :exports none
<<imports>>
def main():
<<analyseur-arguments>>
<<traiter-donnees>>
if __name__ == "__main__":
main()
#+END_SRC
Lors de l'extraction, bin/run_pipeline.py est écrit comme un script propre et exécutable avec tous les fragments correctement placés et indentés !
3.3 Préfixe et Indentation Noweb
- Noweb respecte l'indentation : si
<<analyseur-arguments>>est indenté de 4 espaces à l'intérieur dedef main():, chaque ligne du bloc développé sera indentée de 4 espaces. - Pour désactiver le comportement de préfixe :
:noweb-prefix no. - Pour insérer les résultats de l'exécution d'un bloc au lieu de son code source, ajoutez des parenthèses :
<<compute_constant()>>
3.4 Débogage de l'Expansion
Pour vérifier comment les blocs noweb vont se développer avant l'extraction ou l'exécution :
- Placez le point à l'intérieur du bloc et appuyez sur
C-c C-v v(org-babel-expand-src-block). - Org ouvre un tampon temporaire montrant le code exact après toutes les substitutions noweb et
:var.
4. Intégration de la Configuration et des Données dans les Fichiers Extrait
Babel peut intégrer des variables provenant de tableaux Org ou de propriétés de rubrique directement dans le code extrait :
# Configuration stockée dans un tableau Org :
#+NAME: config_serveur
| Clé | Valeur |
|---------+-----------|
| host | 10.0.1.25 |
| port | 8080 |
| timeout | 30 |
# Le module de configuration extrait :
#+BEGIN_SRC python :tangle config.py :var cfg=config_serveur :colnames yes
HOST = cfg[0][1]
PORT = int(cfg[1][1])
TIMEOUT = int(cfg[2][1])
#+END_SRC
Pour empêcher Babel de générer des wrappers de variables dans le code extrait, passez :no-expand.
5. Gestion de l'Indentation : Langages Sensibles aux Espaces
Dans les langages sensibles aux espaces comme Python ou YAML, la ré-indentation automatique d'Org peut casser la syntaxe.
Règle Critique pour le Tangling Python :
Définissez org-src-preserve-indentation à t. Lorsqu'il est t, Org ne supprime ni n'ajoute d'indentation pendant l'exportation ou l'extraction :
;; Dans la configuration Emacs :
(setq org-src-preserve-indentation t)
Ou par bloc avec le commutateur -i :
#+BEGIN_SRC python -i :tangle script.py
6. Comment Exécuter le Tangling
6.1 De Manière Interactive dans Emacs
| Raccourci | Commande | Description |
|---|---|---|
C-c C-v t / C-c C-v C-t | org-babel-tangle | Extrait tous les blocs de code du tampon actuel. |
C-c C-v f / C-c C-v C-f | org-babel-tangle-file | Demande un fichier Org sur le disque et l'extrait. |
6.2 Navigation des Fichiers Extrait vers l'Org
Lors de l'extraction, Babel insère des commentaires de rétrolien dans les fichiers générés :
# [[file:~/project/notebook.org::*Utilitaires Principaux][core-slugify]]
def slugify(text: str) -> str:
...
# core-slugify se termine ici
Dans Emacs, ouvrez le fichier généré et exécutez M-x org-babel-tangle-jump-to-org pour sauter instantanément vers la rubrique et le bloc exacts dans le document .org source !
6.3 Extraction Headless par Ligne de Commande / CI / Make
Extrayez des fichiers sans ouvrir une fenêtre graphique Emacs (idéal pour les scripts de build, les Makefiles et l'intégration continue) :
# Extraire un seul fichier :
emacs -Q --batch \
--eval "(require 'org)" \
--eval "(require 'ob-tangle)" \
--eval '(org-babel-tangle-file "analysis.org")'
# Extraire plusieurs fichiers depuis une boucle shell ou un Makefile :
emacs -Q --batch --eval "
(progn
(require 'org)
(require 'ob-tangle)
(dolist (file command-line-args-left)
(with-current-buffer (find-file-noselect file)
(org-babel-tangle))))" *.org
7. Gestion Littérale de la Configuration : Le Modèle init.org
Un cas d'usage populaire de la programmation lettrée avec Org Babel est la gestion des configurations Emacs ou des dotfiles système.
Dans ~/.emacs.d/init.el :
;; Amorçage de la configuration lettrée Org Babel
(require 'org)
(require 'ob-tangle)
(org-babel-load-file (expand-file-name "config.org" user-emacs-directory))
org-babel-load-file vérifie automatiquement si config.org a été modifié depuis la dernière génération de config.el, extrait la nouvelle version si nécessaire, et charge le fichier .el résultant.
8. Liste de Vérification pour les Projets de Tangling
- Chemins d'extraction explicites : Utilisez des noms de fichiers explicites (ex :
:tangle "src/core.py"). - Utilisez Noweb pour la Lisibilité : Décomposez les algorithmes complexes en étapes logiques (
<<init-poids>>,<<passe-avant>>,<<retropropagation>>) afin que le récit soit fluide pour les lecteurs. - Préservez l'Indentation : Assurez-vous toujours que
org-src-preserve-indentationesttpour le code Python. - Automatisez avec Make/CI : Incluez une étape d'extraction headless
emacs --batchdans votre système de build pour maintenir le code source généré synchronisé avec le document Org.