Literarisches Programmieren & Tangling mit Org Babel
Org Babel ist eine Implementierung von Donald Knuths Literate Programming-Paradigma. Anstatt Code mit vereinzelten Kommentaren zu schreiben, verfasst man menschenorientierte Prosa, Architekturnotizen und Designentscheidungen und bettet die funktionalen Code-Blöcke darin ein.
Eine einzige Org-Datei dient als endgültige Quelle der Wahrheit für zwei unterschiedliche Ausgaben:
- Weaving (Menschliche Dokumentation): Export des Notebooks in formatiertes HTML, LaTeX/PDF oder Markdown für Leser.
- Tangling (Maschinenausführung): Extrahieren von reinen, ausführbaren Quellcodedateien (
.py,.sh,.c,.conf,.el) für Compiler, Interpreter und Bereitstellungssysteme.
1. Grundlagen des Tangelings: Das :tangle-Header-Argument
Standardmäßig werden Code-Blöcke nicht getangelt. Die :tangle-Familie von Header-Argumenten gibt an, wo und wie Blöcke auf die Festplatte extrahiert werden.
1.1 Die :tangle-Argumentwerte
| Wert | Verhalten |
|---|---|
:tangle no (Standard) | Block wird in keine Datei geschrieben. (Kann dennoch über <<noweb>> in anderen Blöcken eingefügt werden.) |
:tangle "path/to/file.py" | Schreibt den Blockkörper direkt in die angegebene Datei (relativ zum Puffer-Verzeichnis oder absolut). |
:tangle yes | Tangelt in eine Datei, die dem Basisnamen der Org-Datei mit der standardmäßigen Dateierweiterung der Sprache entspricht (z. B. model.org -> model.py). |
1.2 Zusätzliche Tangle-Header-Argumente
:shebang "#!/usr/bin/env python3": Fügt die angegebene Shebang-Zeichenkette als erste Zeile der getangelten Datei ein.:tangle-mode (identity #o755): Setzt Dateisystemberechtigungen für die generierte Datei (z. B.#o755macht ein Skript ausführbar).:no-expand: Verhindert, dass Variablenzuweisungen (:var) in den getangelten Code eingefügt werden; schreibt nur den rohen Blockkörper.:mkdirp yes: Erstellt automatisch alle nicht existierenden übergeordneten Verzeichnisse im Tangle-Pfad.
2. Tangle-Geltungsbereich: Block-, Unterbaum- und Pufferebene
Tangle-Argumente können sauber über ein gesamtes Projekt hinweg definiert werden:
2.1 Unterbaum-Ebene Tangling (Empfohlenes Muster)
Verschiedene Überschriften können in unterschiedliche Dateien oder Module tangeln:
* Kernfunktionen
: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
* Datenverarbeitungspipeline
: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 Pufferweiter Tangle-Standard
Am Anfang der Datei setzen:
#+PROPERTY: header-args:python :tangle src/main.py
3. Noweb-Syntax: Modulare Code-Zusammenstellung
Noweb ermöglicht es Ihnen, größere Quelldateien aus kleineren, modularen, leicht erklärbaren Fragmenten zusammenzusetzen. Sie verweisen mit <<block_name>> auf einen Code-Block innerhalb eines anderen.
3.1 Noweb-Erweiterung aktivieren
Noweb ist standardmäßig deaktiviert. Steuern Sie es mit dem :noweb-Header-Argument:
:noweb-Wert | In Emacs ausgewertet? | Auf Festplatte getangelt? | Im Dokument exportiert? |
|---|---|---|---|
no (Standard) | Nein | Nein | Nein |
yes | Ja | Ja | Ja |
tangle | Nein | Ja | Nein |
strip-tangle | Ja | Nein (Referenzen entfernt) | Ja |
no-export | Ja | Ja | Nein |
eval | Ja | Nein | Nein |
3.2 Noweb-Beispiel: Literarische Skriptkonstruktion
#+NAME: imports
#+BEGIN_SRC python :exports none
import sys
import argparse
from pathlib import Path
#+END_SRC
#+NAME: argument-parser
#+BEGIN_SRC python :exports code
parser = argparse.ArgumentParser(description="Prozessdatensatz.")
parser.add_argument("--input", required=True, type=Path, help="Pfad zur CSV-Eingabe")
parser.add_argument("--threshold", default=0.5, type=float, help="Score-Grenzwert")
args = parser.parse_args()
#+END_SRC
#+NAME: process-data
#+BEGIN_SRC python :exports code
print(f"Lade Daten von {args.input}...")
# Verarbeitungslogik hier
#+END_SRC
# Zusammenstellung des endgültigen ausführbaren Skripts:
#+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():
<<argument-parser>>
<<process-data>>
if __name__ == "__main__":
main()
#+END_SRC
Beim Tangling wird bin/run_pipeline.py als sauberes, ausführbares Skript mit allen korrekt platzierten und eingerückten Fragmenten geschrieben!
3.3 Noweb-Präfix & Einrückung
- Noweb beachtet Einrückungen: Wenn
<<argument-parser>>innerhalb vondef main():um 4 Leerzeichen eingerückt ist, wird jede Zeile des expandierten Blocks um 4 Leerzeichen eingerückt. - Zum Deaktivieren des Präfixverhaltens:
:noweb-prefix no. - Um die Ergebnisse der Ausführung eines Blocks anstelle seines Quellcodes einzufügen, fügen Sie Klammern an:
<<compute_constant()>>
3.4 Debuggen der Expansion
Um zu überprüfen, wie Noweb-Blöcke vor dem Tangling oder Ausführen expandiert werden:
- Setzen Sie den Punkt innerhalb des Blocks und drücken Sie
C-c C-v v(org-babel-expand-src-block). - Org öffnet einen temporären Puffer, der den genauen Code nach allen Noweb- und
:var-Substitutionen anzeigt.
4. Einbetten von Konfiguration und Daten in getangelte Dateien
Babel kann Variablen aus Org-Tabellen oder Überschrifteneigenschaften direkt in getangelten Code einbetten:
# Konfiguration, gespeichert in einer Org-Tabelle:
#+NAME: server_config
| Key | Value |
|---------+------------------|
| host | 10.0.1.25 |
| port | 8080 |
| timeout | 30 |
# Das getangelte Konfigurationsmodul:
#+BEGIN_SRC python :tangle config.py :var cfg=server_config :colnames yes
HOST = cfg[0][1]
PORT = int(cfg[1][1])
TIMEOUT = int(cfg[2][1])
#+END_SRC
Um zu verhindern, dass Babel Variablen-Wrapper im getangelten Code erzeugt, übergeben Sie :no-expand.
5. Einrückungsbehandlung: Leerraum-empfindliche Sprachen
In leerraum-empfindlichen Sprachen wie Python oder YAML kann die automatische Org-Neueinrückung die Syntax beschädigen.
Wichtige Regel für Python-Tangling:
Setzen Sie org-src-preserve-indentation auf t. Wenn t, entfernt oder fügt Org keine Einrückung während des Exports oder Tangelings hinzu:
;; In der Emacs-Konfiguration:
(setq org-src-preserve-indentation t)
Oder pro Block mit dem Schalter -i:
#+BEGIN_SRC python -i :tangle script.py
6. Wie man Tangling ausführt
6.1 Interaktiv in Emacs
| Tastenkombination | Befehl | Beschreibung |
|---|---|---|
C-c C-v t / C-c C-v C-t | org-babel-tangle | Tangelt alle Code-Blöcke im aktuellen Puffer. |
C-c C-v f / C-c C-v C-f | org-babel-tangle-file | Fragt nach einer Org-Datei auf der Festplatte und tangelt sie. |
6.2 Navigation von getangelten Dateien zurück zum Org
Beim Tangling fügt Babel Rückverweis-Kommentare in generierte Dateien ein:
# [[file:~/project/notebook.org::*Kernfunktionen][core-slugify]]
def slugify(text: str) -> str:
...
# core-slugify endet hier
In Emacs: Öffnen Sie die generierte Datei und führen Sie M-x org-babel-tangle-jump-to-org aus, um sofort zur genauen Überschrift und zum Block im Quell-.org-Dokument zu springen!
6.3 Kopflose Kommandozeile / CI / Make Batch-Tangling
Tangle-Dateien ohne grafische Emacs-Oberfläche (ideal für Build-Skripte, Makefiles und Continuous Integration):
# Einzelne Datei tangeln:
emacs -Q --batch \
--eval "(require 'org)" \
--eval "(require 'ob-tangle)" \
--eval '(org-babel-tangle-file "analysis.org")'
# Mehrere Dateien aus einer Shell-Schleife oder einem Makefile tangeln:
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. Literarisches Konfigurationsmanagement: Das init.org-Muster
Ein häufiger Anwendungsfall für Org Babel Literate Programming ist die Verwaltung von Emacs-Konfigurationen oder System-Dotfiles.
In ~/.emacs.d/init.el:
;; Bootstrap der literarischen Org Babel-Konfiguration
(require 'org)
(require 'ob-tangle)
(org-babel-load-file (expand-file-name "config.org" user-emacs-directory))
org-babel-load-file prüft automatisch, ob config.org seit der letzten Generierung von config.el geändert wurde, tangelt bei Bedarf die neue Version und lädt die resultierende .el-Datei.
8. Zusammenfassungs-Checkliste für Tangle-Projekte
- Explizite Tangle-Pfade: Verwenden Sie explizite Dateinamen (z. B.
:tangle "src/core.py"). - Noweb für Lesbarkeit verwenden: Zerlegen Sie komplexe Algorithmen in logische Schritte (
<<init-weights>>,<<forward-pass>>,<<backprop>>), damit der Erzählfluss für Leser natürlich bleibt. - Einrückung bewahren: Stellen Sie sicher, dass
org-src-preserve-indentationfür Python-Code auftgesetzt ist. - Mit Make/CI automatisieren: Fügen Sie einen kopflosen
emacs --batch-Tangling-Schritt in Ihr Build-System ein, um generierten Quellcode mit dem Org-Dokument synchron zu halten.