--- name: cache_to_file kind: function lang: py domain: infra version: "1.0.0" purity: impure signature: "def cache_to_file(cache_dir: str, namespace: str = 'default') -> FileCache" description: "Cache key-value donde cada entry es un archivo JSON en disco. Keys se hashean con SHA-256 para generar nombres de archivo seguros. Metadata (ttl, created_at, original_key) en sidecar .meta. Mejor que SQLite para valores grandes (PDFs procesados, embeddings)." tags: [cache, file, persistence, ttl, key-value, sha256] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: ["os", "json", "hashlib", "time", "threading"] params: - name: cache_dir desc: "directorio raiz donde se almacenan los archivos de cache" - name: namespace desc: "namespace logico dentro de cache_dir para aislar diferentes cachés" output: "instancia FileCache con metodos set, get, get_or_set, clear y stats" tested: true tests: - "Set y get basico" - "TTL expirado → None" - "Archivo .meta con metadata correcta" - "Clear elimina el directorio del namespace" - "Key con caracteres especiales → hash seguro" test_file_path: "python/functions/infra/cache_to_file_test.py" file_path: "python/functions/infra/cache_to_file.py" --- ## Ejemplo ```python from infra.cache_to_file import cache_to_file store = cache_to_file("/tmp/my_cache", namespace="embeddings") # Almacenar un embedding grande store.set("doc:123", embedding_vector, ttl=86400) # Recuperar vec = store.get("doc:123") # Factory pattern result = store.get_or_set( "pdf:page_42", factory=lambda: extract_pdf_text("doc.pdf", page=42), ttl=0, # sin expiracion ) ``` ## Estructura en disco ``` cache_dir/ namespace/ {sha256_key}.json # valor serializado como JSON {sha256_key}.meta # {"created_at": ..., "expires_at": ..., "original_key": ...} ``` ## Notas Cada entry genera exactamente dos archivos: `.json` para el valor y `.meta` para la metadata. La key original se guarda en `.meta["original_key"]` para facilitar debugging. Thread-safe mediante `threading.Lock`. La eviction es lazy: se verifica expires_at al hacer `get`.