Spaces:
Running
Running
| """Cache en memoria del DataFrame del preprocessed. | |
| ``pd.read_csv`` de un archivo de 2.47 GB tarda ~60 s en CPU básico. Si lo | |
| guardamos en RAM, la primera corrida sigue tardando lo mismo, pero las que | |
| vienen después son instantáneas (parte CPU del pipeline). | |
| La key del cache incluye ``mtime`` para que, si el archivo se actualiza en | |
| disco (sync semanal del preprocessed), la entrada se invalide sola. | |
| Single-container HF Space, sin multiproceso → un dict global con lock alcanza. | |
| """ | |
| from __future__ import annotations | |
| import threading | |
| from collections import OrderedDict | |
| from pathlib import Path | |
| import pandas as pd | |
| _CACHE: "OrderedDict[tuple[str, float, bool], pd.DataFrame]" = OrderedDict() | |
| _LOCK = threading.Lock() | |
| _MAX_ENTRIES = 2 # preprocessed base + (eventual) labeled distinto | |
| def get_cached(path: Path, *, low_memory: bool = False) -> pd.DataFrame: | |
| """Devuelve un DataFrame del CSV, leyendo del cache si está disponible. | |
| Importante: el DataFrame es **compartido**. Los callers que vayan a mutar | |
| columnas deben hacer ``.copy()`` antes. Mutaciones tipo ``df[col] = ...`` | |
| sobre el devuelto afectan al cache. | |
| """ | |
| p = Path(path).resolve() | |
| key = (str(p), p.stat().st_mtime, low_memory) | |
| with _LOCK: | |
| hit = _CACHE.get(key) | |
| if hit is not None: | |
| _CACHE.move_to_end(key) | |
| return hit | |
| # Leer fuera del lock para no bloquear otros lookups durante 60 s. | |
| df = pd.read_csv(p, low_memory=low_memory) | |
| with _LOCK: | |
| # Si otro thread cargó la misma key en el medio, preferimos su copia. | |
| existing = _CACHE.get(key) | |
| if existing is not None: | |
| return existing | |
| _CACHE[key] = df | |
| _CACHE.move_to_end(key) | |
| while len(_CACHE) > _MAX_ENTRIES: | |
| _CACHE.popitem(last=False) | |
| return df | |
| def is_cached(path: Path, *, low_memory: bool = False) -> bool: | |
| """Si el archivo ya está cargado en RAM (sin tocar disco más allá del stat).""" | |
| p = Path(path).resolve() | |
| if not p.exists(): | |
| return False | |
| key = (str(p), p.stat().st_mtime, low_memory) | |
| with _LOCK: | |
| return key in _CACHE | |
| def clear() -> None: | |
| with _LOCK: | |
| _CACHE.clear() | |