Watch at EaseHistorias. Respuestas. Perspectiva.

Tecnología · GUÍA PRÁCTICA

¿SpooledTemporaryFile tocó disco o se leyó vacío? Audita rollover y posición

Separa el rollover oculto por fileno del cursor en EOF antes de entregar el stream.

Tres triggers de rollover de SpooledTemporaryFile
Ilustración original: fileno puede llevar un payload pequeño al disco. Watch at Ease

SpooledTemporaryFile tiene dos trampas independientes: fileno() puede llevar un payload pequeño al disco y un cursor dejado en EOF puede dar una lectura vacía al consumidor.

Usa API pública para identificar el trigger, haz seek(0), define un contrato de bytes o descriptor y prueba entrega y política de almacenamiento en el consumidor real.

1. Separa estado de almacenamiento y posición

Registra el objeto entregado, max_size, bytes escritos, tell() y llamadas a fileno(). Puede seguir en memoria y estar en EOF, o estar en disco y conservar el mismo EOF.

No uses _file ni _rolled privados como contrato; observa el directorio temporal en una prueba aislada y envuelve solo el límite que controlas.

2. Identifica el trigger de rollover

Python documenta tres triggers: superar un max_size no nulo, llamar fileno() o llamar rollover(). En CPython actual, el valor 0 desactiva el trigger por tamaño, no los otros dos.

Audita la ruta real de biblioteca, framework o subprocess. Una API de descriptor puede llamar fileno() de forma oculta y es incompatible con una política sin disco.

3. Reinicia el cursor antes de entregar

Tras escribir, llama seek(0) antes del siguiente lector. El stdin vacío del issue de CPython tenía el cursor al final; rollover no promete rebobinarlo.

Con FastAPI UploadFile usa await upload.seek(0) en async; en sync usa el método público permitido y define quién hace el close final.

4. Elige un contrato de entrega

Si acepta bytes, usa pipe, BytesIO o el resultado de read para conservar no-disk. Si exige descriptor, haz rollover explícito, confirma espacio, seek(0), entrega y cierre por el owner.

Para salida duradera copia a un destino elegido y define fsync o atomic replace; un temporary file no es una interfaz de persistencia.

5. Prueba ambos ejes en el límite real

Escribe un payload distintivo, anota tell(), rebobina, llama al consumidor real y compara el resultado. Ejecuta casos bajo umbral y con rollover forzado.

Añade un test double que registre fileno() y observa el directorio temporal aislado. Valida entrega, política de almacenamiento y limpieza por separado.

Entrega y aceptación del cursor del stream
Ilustración original: rebobina y verifica bytes en el consumidor real. Watch at Ease