Programação Literária & Tangling com Org Babel
Org Babel é uma implementação do paradigma de Programação Literária de Donald Knuth. Em vez de escrever código com comentários intercalados, você escreve prosa orientada a humanos, notas de arquitetura e justificativas de design, incorporando os blocos de código funcionais dentro dela.
Um único arquivo Org serve como a fonte definitiva da verdade para duas saídas distintas:
- Weaving (Documentação Humana): Exportar o caderno para HTML formatado, LaTeX/PDF ou Markdown para leitores.
- Tangling (Execução da Máquina): Extrair arquivos de código-fonte executáveis puros (
.py,.sh,.c,.conf,.el) para compiladores, interpretadores e sistemas de implantação.
1. Noções Básicas de Tangling: O Argumento de Cabeçalho :tangle
Por padrão, os blocos de código não são "tangled" (extraídos). A família de argumentos de cabeçalho :tangle especifica onde e como os blocos são extraídos para o disco.
1.1 Valores do Argumento :tangle
| Valor | Comportamento |
|---|---|
:tangle no (Padrão) | O bloco não é gravado em nenhum arquivo. (Ele ainda pode ser incluído dentro de outros blocos via <<noweb>>). |
:tangle "caminho/para/arquivo.py" | Grava o corpo do bloco diretamente no caminho de arquivo especificado (relativo ao diretório do buffer ou absoluto). |
:tangle yes | "Tangla" para um arquivo com o nome base do arquivo Org e a extensão de arquivo padrão da linguagem (ex.: model.org -> model.py). |
1.2 Argumentos de Cabeçalho Adicionais para Tangling
:shebang "#!/usr/bin/env python3": Adiciona a string shebang especificada como a primeira linha do arquivo "tangled".:tangle-mode (identity #o755): Define as permissões do sistema de arquivos no arquivo gerado (ex.:#o755torna um script executável).:no-expand: Impede que atribuições de variáveis (:var) sejam inseridas no código "tangled"; escreve apenas o corpo bruto do bloco.:mkdirp yes: Cria automaticamente quaisquer diretórios pai inexistentes no caminho do "tangle".
2. Escopo do Tangling: Nível de Bloco, Subárvore e Buffer
Os argumentos de "tangling" podem ser definidos de forma limpa em todo um projeto:
2.1 Tangling em Nível de Subárvore (Padrão Recomendado)
Diferentes títulos podem "tangle" para diferentes arquivos ou módulos:
* Utilitários Principais
:PROPERTIES:
:header-args:python: :tangle src/utils.py :mkdirp yes
:END:
#+BEGIN_SRC python
def slugify(texto: str) -> str:
return "-".join(texto.lower().split())
#+END_SRC
* Pipeline de Processamento de Dados
: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 Padrão de Tangling em Nível de Buffer
Defina no topo do arquivo:
#+PROPERTY: header-args:python :tangle src/main.py
3. Sintaxe Noweb: Montagem Modular de Código
Noweb permite montar arquivos de código-fonte maiores a partir de fragmentos menores, modulares e facilmente explicados. Você referencia um bloco de código dentro de outro usando <<nome_do_bloco>>.
3.1 Ativando a Expansão Noweb
Noweb está desabilitado por padrão. Controle-o com o argumento de cabeçalho :noweb:
Valor :noweb | Avaliado no Emacs? | "Tangled" para o Disco? | Exportado no Documento? |
|---|---|---|---|
no (Padrão) | Não | Não | Não |
yes | Sim | Sim | Sim |
tangle | Não | Sim | Não |
strip-tangle | Sim | Não (referências removidas) | Sim |
no-export | Sim | Sim | Não |
eval | Sim | Não | Não |
3.2 Exemplo Noweb: Construção de Script Literário
#+NAME: imports
#+BEGIN_SRC python :exports none
import sys
import argparse
from pathlib import Path
#+END_SRC
#+NAME: analisador-de-argumentos
#+BEGIN_SRC python :exports code
parser = argparse.ArgumentParser(description="Processa conjunto de dados.")
parser.add_argument("--input", required=True, type=Path, help="Caminho do CSV de entrada")
parser.add_argument("--threshold", default=0.5, type=float, help="Limiar de pontuação")
args = parser.parse_args()
#+END_SRC
#+NAME: processar-dados
#+BEGIN_SRC python :exports code
print(f"Carregando dados de {args.input}...")
# Lógica de processamento aqui
#+END_SRC
# Montando o script executável 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():
<<analisador-de-argumentos>>
<<processar-dados>>
if __name__ == "__main__":
main()
#+END_SRC
Quando "tangled", bin/run_pipeline.py é escrito como um script limpo e executável com todos os fragmentos propriamente posicionados e indentados!
3.3 Prefixo e Indentação Noweb
- Noweb respeita a indentação: se
<<analisador-de-argumentos>>estiver indentado por 4 espaços dentro dedef main():, cada linha do bloco expandido será indentada por 4 espaços. - Para desabilitar o comportamento de prefixo:
:noweb-prefix no. - Para inserir os resultados da execução de um bloco em vez de seu código-fonte, acrescente parênteses:
<<compute_constant()>>
3.4 Depuração da Expansão
Para verificar como os blocos noweb serão expandidos antes de "tangle" ou executar:
- Posicione o ponto dentro do bloco e pressione
C-c C-v v(org-babel-expand-src-block). - O Org abre um buffer temporário mostrando o código exato após todas as substituições noweb e
:var.
4. Inserindo Configuração e Dados em Arquivos "Tangled"
O Babel pode inserir variáveis de tabelas Org ou propriedades de título diretamente no código "tangled":
# Configuração armazenada em uma Tabela Org:
#+NAME: config_servidor
| Chave | Valor |
|---------+-----------|
| host | 10.0.1.25 |
| porta | 8080 |
| timeout | 30 |
# O módulo de configuração "tangled":
#+BEGIN_SRC python :tangle config.py :var cfg=config_servidor :colnames yes
HOST = cfg[0][1]
PORTA = int(cfg[1][1])
TIMEOUT = int(cfg[2][1])
#+END_SRC
Para evitar que o Babel gere wrappers de variáveis no código "tangled", passe :no-expand.
5. Tratamento de Indentação: Linguagens Sensíveis a Espaços em Branco
Em linguagens sensíveis a espaços em branco como Python ou YAML, a reindentação automática do Org pode quebrar a sintaxe.
Regra Crítica para Tangling em Python:
Defina org-src-preserve-indentation como t. Quando t, o Org não remove nem adiciona indentação durante exportação ou "tangling":
;; Na configuração do Emacs:
(setq org-src-preserve-indentation t)
Ou por bloco com o switch -i:
#+BEGIN_SRC python -i :tangle script.py
6. Como Executar o Tangling
6.1 Interativamente Dentro do Emacs
| Atalho | Comando | Descrição |
|---|---|---|
C-c C-v t / C-c C-v C-t | org-babel-tangle | "Tangla" todos os blocos de código no buffer atual. |
C-c C-v f / C-c C-v C-f | org-babel-tangle-file | Solicita um arquivo Org no disco e o "tangla". |
6.2 Navegando de Arquivos "Tangled" de Volta para o Org
Quando "tangled", o Babel insere comentários de back-link nos arquivos gerados:
# [[file:~/project/notebook.org::*Utilitários Principais][core-slugify]]
def slugify(texto: str) -> str:
...
# core-slugify ends here
No Emacs, abra o arquivo gerado e execute M-x org-babel-tangle-jump-to-org para saltar instantaneamente de volta para o título e bloco exatos no documento .org de origem!
6.3 Tangling em Lote Headless / CLI / CI / Make
"Tangle" arquivos sem abrir uma janela GUI do Emacs (ideal para scripts de build, Makefiles e integração contínua):
# Tangla um único arquivo:
emacs -Q --batch \
--eval "(require 'org)" \
--eval "(require 'ob-tangle)" \
--eval '(org-babel-tangle-file "analysis.org")'
# Tangla vários arquivos a partir de um loop shell ou 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. Gerenciamento de Configuração Literária: Padrão init.org
Um caso de uso popular para programação literária com Org Babel é gerenciar configurações do Emacs ou dotfiles do sistema.
Em ~/.emacs.d/init.el:
;; Inicializa configuração literária do Org Babel
(require 'org)
(require 'ob-tangle)
(org-babel-load-file (expand-file-name "config.org" user-emacs-directory))
org-babel-load-file verifica automaticamente se config.org foi modificado desde que config.el foi gerado pela última vez, "tangla" a nova versão se necessário e carrega o arquivo .el resultante.
8. Lista de Verificação Resumida para Projetos com Tangling
- Caminhos de Tangling Explícitos: Use nomes de arquivo explícitos (ex.:
:tangle "src/core.py"). - Use Noweb para Legibilidade: Divida algoritmos complexos em etapas lógicas (
<<init-weights>>,<<forward-pass>>,<<backprop>>) para que a narrativa flua naturalmente para os leitores. - Preserve a Indentação: Sempre certifique-se de que
org-src-preserve-indentationesteja comotpara código Python. - Automatize com Make/CI: Inclua uma etapa de "tangling" headless
emacs --batchem seu sistema de build para manter o código-fonte gerado sincronizado com o documento Org.