gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Sutichaykuna

⎇ 3137d47f165b900042653af6db5513180fe471c8 scripts/mudanza/modelo.py Python · 79 siq'ikuna · 3.2 KB Ñawpa willay Pi imata llamkarqan ch'uya

traducir: CENTRO de traducción con modelo pivote y plugins — N+M en vez de N×M 3137d47f1 · Sergio ·

1#!/usr/bin/env python3
2"""modelo.py — el MODELO PIVOTE de configuración de servicio, y el acta que lo acompaña (SDD 29).
3
4── POR QUÉ UN PIVOTE Y NO PARES ────────────────────────────────────────────────────────────────
5Traducir de a pares crece en N×M: con nginx, apache, caddy, haproxy y lighttpd son 20 traductores, y
6cada formato nuevo agrega 2N. Con un modelo intermedio son **N lectores + M escritores**: un formato
7nuevo cuesta UNO o DOS plugins, y estrena todos los pares de golpe. Es la diferencia entre un centro
8de traducción y una pila de conversores.
9
10── EL ACTA VIAJA DENTRO DEL MODELO, NO AL COSTADO ──────────────────────────────────────────────
11Ésta es la decisión que hace que el pivote no mienta. Si el acta fuera un efecto de cada traductor,
12lo que un LECTOR no entendió se perdería antes de llegar al escritor. Viajando en el modelo, el
13escritor puede emitirlo como comentario y el humano lo ve en el fichero que va a desplegar.
14
15Doctrina heredada de `soltar`/`paskaq` (tawasuyu): **elisión honesta** — lo que no se pudo traducir
16se reporta con su línea y su motivo, nunca se descarta en silencio.
17"""
18from dataclasses import dataclass, field
19
20
21@dataclass
22class Accion:
23    """Qué hace una ruta. `tipo` ∈ {proxy, archivos, redir, responder, intentar}."""
24    tipo: str
25    valor: str = ""
26    extra: dict = field(default_factory=dict)
27
28
29@dataclass
30class Ruta:
31    """Un camino dentro de un sitio. `clase` ∈ {prefijo, exacto, regex}.
32
33    `regex` se conserva a propósito aunque casi ningún destino lo soporte igual: **perder la
34    distinción sería traducir mal en silencio**. El escritor decide si sabe emitirla o la manda al
35    acta."""
36    patron: str
37    clase: str = "prefijo"
38    acciones: list = field(default_factory=list)
39    origen: int = 0
40
41
42@dataclass
43class Sitio:
44    nombres: list = field(default_factory=list)
45    puertos: list = field(default_factory=list)      # "80", "443"
46    tls: dict = field(default_factory=dict)          # {cert, key}
47    raiz: str = ""
48    indices: list = field(default_factory=list)
49    rutas: list = field(default_factory=list)
50    acciones: list = field(default_factory=list)     # a nivel de sitio
51    origen: int = 0
52
53
54@dataclass
55class Acta:
56    """Registro de procedencia. Cuatro estados, y el que importa es el primero."""
57    entradas: list = field(default_factory=list)
58
59    def anota(self, linea, estado, texto, motivo=""):
60        # estado ∈ {SIN-TRADUCIR, decision, traducido, no-aplica}
61        self.entradas.append({"linea": linea, "estado": estado,
62                              "directiva": texto, "motivo": motivo})
63
64    def sin_traducir(self):
65        return [e for e in self.entradas if e["estado"] == "SIN-TRADUCIR"]
66
67    def por_estado(self):
68        d = {}
69        for e in self.entradas:
70            d.setdefault(e["estado"], []).append(e)
71        return d
72
73
74@dataclass
75class Config:
76    """Lo que un lector produce y un escritor consume."""
77    formato_origen: str = ""
78    sitios: list = field(default_factory=list)
79    acta: Acta = field(default_factory=Acta)