Tecnología · GUÍA PRÁCTICA
Python NamedTemporaryFile en Windows: reabrir y limpiar bien
El primer PermissionError y el fallo posterior al borrar tienen causas distintas. Elige el límite de borrado y cierra antes todos los identificadores secundarios.

NamedTemporaryFile puede fallar en dos momentos de Windows. Con los valores predeterminados, otro programa quizá no pueda abrir la ruta. Tras cambiar las opciones, puede leerla pero conservar un identificador abierto, de modo que la limpieza falle al salir del contexto.
Trata la ruta temporal como una transferencia de propiedad. Decide si el borrado corresponde al cierre, a la salida del contexto o a tu bloque finally; después termina todos los consumidores antes de ese límite.
1. Distingue qué PermissionError tienes
Si el subproceso, navegador o cliente de carga no puede abrir la ruta, el creador conserva un identificador de Windows cuyas condiciones no coinciden con la segunda apertura. CPython usa O_TEMPORARY en Windows con delete=True y delete_on_close=True; open y muchos programas externos no solicitan el mismo acceso compartido de borrado.
Si el consumidor lee el archivo pero fallan la salida del contexto u os.unlink, busca otro identificador que siga abierto. Microsoft documenta que el modo compartido dura hasta cerrar el identificador y que el borrado espera a los identificadores compatibles. Cambiar una opción no cierra por ti un flujo de carga o subproceso.
2. Prefiere la limpieza al salir del contexto en Python 3.12+
Para una apertura externa normal usa with NamedTemporaryFile(delete=True, delete_on_close=False) as tmp:. Escribe y haz flush si el consumidor lee mientras tmp sigue abierto, o cierra tmp dentro del bloque si necesita acceso exclusivo. La ruta permanece hasta salir de with, cuando Python intenta os.unlink.
Espera al subproceso y cierra todo objeto abierto para una carga antes de salir. Una segunda apertura normal que no comparta borrado debe cerrarse primero. Si el directorio no concede permiso de borrado, unlink aún puede lanzar PermissionError; compruébalo en el directorio real y no ocultes el error.
3. Usa finally en versiones anteriores
Antes de Python 3.12, o cuando una biblioteca obliga a cerrar el envoltorio antes de entregar la ruta, usa delete=False. Cierra NamedTemporaryFile, llama al consumidor y elimina la ruta en finally. Con delete=False, delete_on_close se ignora y toda la limpieza queda bajo tu responsabilidad.
Guarda la ruta solo tras crearla con éxito. En finally cierra cualquier flujo secundario antes de os.unlink. No uses tempfile.mktemp para evitar el problema: devuelve un nombre sin crear el archivo de forma segura e introduce una carrera.
4. Audita todo el ciclo de los identificadores
Antes de la entrega, confirma que la escritura se vació o que el creador se cerró según lo necesite el consumidor y que la ruta existe. Espera al consumidor; no lo lances de forma asíncrona y salgas enseguida. Si una biblioteca acepta un objeto de archivo, envuelve open(tmp.name, 'rb') en su propio with.
Antes de limpiar, revisa objetos de archivo, subprocesos y mapas de memoria. Después, afirma que la ruta desapareció o muestra el error exacto. Un bucle de reintento puede esconder un identificador abierto; no sustituye a averiguar quién conserva el archivo.
5. Mantén explícito el límite de portabilidad
POSIX suele permitir reabrir un archivo temporal con nombre mientras está abierto, pero eso no demuestra que el ciclo de Windows sea seguro. Usa la misma entrega y limpieza explícitas en todas las plataformas para evitar pruebas verdes en macOS/Linux y fallos en Windows.
Las opciones difieren ante una caída. El borrado al cerrar es fuerte si todos comparten borrado; la limpieza al salir facilita programas normales pero puede interrumpirse; delete=False ofrece más compatibilidad, pero exige finally y limpieza de restos al iniciar. Prueba tanto el fallo del consumidor como el de limpieza.
