tempfile — Objetos temporales del sistema de archivos

Propósito:Crear objetos temporales del sistema de archivos.

Crear archivos temporales con nombres únicos de forma segura, que no pueden ser adivinados por alguien que quiere romper la aplicación o robar los datos, es un reto. El módulo tempfile proporciona varias funciones para crear de forma segura los recursos temporales del sistema de archivos. TemporaryFile() abre y devuelve un archivo sin nombre, NamedTemporaryFile() abre y devuelve un archivo con nombre, SpooledTemporaryFile guarda su contenido en la memoria antes escribirlo en el disco, y TemporaryDirectory es un gestor de contexto que elimina el directorio cuando se cierra el contexto.

Archivos temporales

Aplicaciones que necesitan archivos temporales para almacenar datos, sin necesidad de compartir ese archivo con otros programas, debe usar la función TemporaryFile() para crear los archivos. La función crea un archivo, y en las plataformas donde es posible, lo desvincula inmediatamente. Esto hace que sea imposible que otro programa encuentre o abra el archivo, ya que no hay ninguna referencia a él en el sistema de archivos. El archivo creado por TemporaryFile() se elimina automáticamente cuando se cierra, ya sea llamando a close() o usando la interfaz del gestor de contexto y la declaración with.

tempfile_TemporaryFile.py
import os
import tempfile

print('Building a filename with PID:')
filename = '/tmp/guess_my_name.{}.txt'.format(os.getpid())
with open(filename, 'w+b') as temp:
    print('temp:')
    print('  {!r}'.format(temp))
    print('temp.name:')
    print('  {!r}'.format(temp.name))

# Clean up the temporary file yourself.
os.remove(filename)

print()
print('TemporaryFile:')
with tempfile.TemporaryFile() as temp:
    print('temp:')
    print('  {!r}'.format(temp))
    print('temp.name:')
    print('  {!r}'.format(temp.name))

# Automatically cleans up the file.

Este ejemplo ilustra la diferencia en la creación de un archivo temporal usando un patrón común para crear un nombre, en lugar de usar la función TemporaryFile(). El archivo devuelto por TemporaryFile() no tiene nombre.

$ python3 tempfile_TemporaryFile.py

Building a filename with PID:
temp:
  <_io.BufferedRandom name='/tmp/guess_my_name.12151.txt'>
temp.name:
  '/tmp/guess_my_name.12151.txt'

TemporaryFile:
temp:
  <_io.BufferedRandom name=4>
temp.name:
  4

De forma predeterminada, el identificador de archivo se crea con el modo 'w+b' por lo que se comporta de manera consistente en todas las plataformas y la persona que llama puede escribir y leer de él.

tempfile_TemporaryFile_binary.py
import os
import tempfile

with tempfile.TemporaryFile() as temp:
    temp.write(b'Some data')

    temp.seek(0)
    print(temp.read())

Después de escribir, el identificador de archivo debe ser «rebobinado» usando seek() con el fin de leer los datos de nuevo.

$ python3 tempfile_TemporaryFile_binary.py

b'Some data'

Para abrir el archivo en modo de texto, configura mode con 'w+t' cuando el archivo es creado.

tempfile_TemporaryFile_text.py
import tempfile

with tempfile.TemporaryFile(mode='w+t') as f:
    f.writelines(['first\n', 'second\n'])

    f.seek(0)
    for line in f:
        print(line.rstrip())

El identificador de archivo trata los datos como texto.

$ python3 tempfile_TemporaryFile_text.py

first
second

Archivos con nombre

Hay situaciones donde tener un archivo temporal con nombre es importante. Para aplicaciones que abarcan múltiples procesos, o incluso hosts, nombrar el archivo es la forma más sencilla de pasarlo entre partes de la aplicación. La función NamedTemporaryFile() crea un archivo sin desvincularlo, por lo que conserva su nombre (se accede con el atributo name).

tempfile_NamedTemporaryFile.py
import os
import pathlib
import tempfile

with tempfile.NamedTemporaryFile() as temp:
    print('temp:')
    print('  {!r}'.format(temp))
    print('temp.name:')
    print('  {!r}'.format(temp.name))

    f = pathlib.Path(temp.name)

print('Exists after close:', f.exists())

El archivo se elimina después de cerrar el identificador.

$ python3 tempfile_NamedTemporaryFile.py

temp:
  <tempfile._TemporaryFileWrapper object at 0x1011b2d30>
temp.name:
  '/var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/tmps4qh5zde'
Exists after close: False

Archivos en spool

Para archivos temporales que contienen cantidades relativamente pequeñas de datos, probablemente sea más eficiente usar un SpooledTemporaryFile porque guarda el contenido del archivo en la memoria usando un buffer io.BytesIO o io.StringIO hasta que alcancen un umbral de tamaño . Cuando la cantidad de datos pasa el umbral, se «enrolla» y se escribe al disco, y luego el buffer se sustituye por un TemporaryFile normal.

tempfile_SpooledTemporaryFile.py
import tempfile

with tempfile.SpooledTemporaryFile(max_size=100,
                                   mode='w+t',
                                   encoding='utf-8') as temp:
    print('temp: {!r}'.format(temp))

    for i in range(3):
        temp.write('This line is repeated over and over.\n')
        print(temp._rolled, temp._file)

Este ejemplo usa atributos privados del SpooledTemporaryFile para determinar cuándo ha ocurrido la transferencia al disco. Normalmente no es necesario comprobar este estado, excepto cuando se ajusta el tamaño del buffer.

$ python3 tempfile_SpooledTemporaryFile.py

temp: <tempfile.SpooledTemporaryFile object at 0x1007b2c88>
False <_io.StringIO object at 0x1007a3d38>
False <_io.StringIO object at 0x1007a3d38>
True <_io.TextIOWrapper name=4 mode='w+t' encoding='utf-8'>

Para hacer que el buffer se escriba explícitamente en el disco, llama a los métodos rollover() o fileno().

tempfile_SpooledTemporaryFile_explicit.py
import tempfile

with tempfile.SpooledTemporaryFile(max_size=1000,
                                   mode='w+t',
                                   encoding='utf-8') as temp:
    print('temp: {!r}'.format(temp))

    for i in range(3):
        temp.write('This line is repeated over and over.\n')
        print(temp._rolled, temp._file)
    print('rolling over')
    temp.rollover()
    print(temp._rolled, temp._file)

En este ejemplo, debido a que el tamaño del buffer es mucho más grande que la cantidad de datos, no se crearía ningún archivo en el disco excepto que rollover() sea llamado.

$ python3 tempfile_SpooledTemporaryFile_explicit.py

temp: <tempfile.SpooledTemporaryFile object at 0x1007b2c88>
False <_io.StringIO object at 0x1007a3d38>
False <_io.StringIO object at 0x1007a3d38>
False <_io.StringIO object at 0x1007a3d38>
rolling over
True <_io.TextIOWrapper name=4 mode='w+t' encoding='utf-8'>

Directorios temporales

Cuando se necesitan varios archivos temporales, puede ser más conveniente crear un directorio temporal con TemporaryDirectory y abrir todos los archivos en ese directorio.

tempfile_TemporaryDirectory.py
import pathlib
import tempfile

with tempfile.TemporaryDirectory() as directory_name:
    the_dir = pathlib.Path(directory_name)
    print(the_dir)
    a_file = the_dir / 'a_file.txt'
    a_file.write_text('This file is deleted.')

print('Directory exists after?', the_dir.exists())
print('Contents after:', list(the_dir.glob('*')))

El gestor de contexto produce el nombre del directorio, que luego puede ser utilizado dentro del bloque de contexto para construir otros nombres de archivo.

$ python3 tempfile_TemporaryDirectory.py

/var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T/tmp_urhiioj
Directory exists after? False
Contents after: []

Predicción de nombres

Aunque es menos seguro que los archivos temporales estrictamente anónimos, incluir una parte predecible en el nombre hace posible encontrar el archivo y examinarlo para fines de depuración. Todas las funciones descritas hasta ahora toman tres argumentos para controlar los nombres de archivo hasta cierto punto. Los nombres se generan utilizando la fórmula:

dir + prefix + random + suffix

Todos los valores, excepto random, pueden pasarse como argumentos a las funciones para la creación de archivos o directorios temporales.

tempfile_NamedTemporaryFile_args.py
import tempfile

with tempfile.NamedTemporaryFile(suffix='_suffix',
                                 prefix='prefix_',
                                 dir='/tmp') as temp:
    print('temp:')
    print('  ', temp)
    print('temp.name:')
    print('  ', temp.name)

Los argumentos prefix y suffix se combinan con una cadena aleatoria de caracteres para construir el nombre de archivo, y se toma el argumento dir tal como está y se utiliza como la ubicación del nuevo archivo.

$ python3 tempfile_NamedTemporaryFile_args.py

temp:
   <tempfile._TemporaryFileWrapper object at 0x1018b2d68>
temp.name:
   /tmp/prefix_q6wd5czl_suffix

Ubicación de archivos temporales

Si no se da un destino explícito usando el argumento dir, la ruta utilizada para los archivos temporales variará según la versión actual y los ajustes de plataforma. El módulo tempfile incluye dos funciones para consultar la configuración que se utiliza en tiempo de ejecución.

tempfile_settings.py
import tempfile

print('gettempdir():', tempfile.gettempdir())
print('gettempprefix():', tempfile.gettempprefix())

gettempdir() devuelve el directorio predeterminado que contendrá todos los archivos temporales y gettempprefix() devuelve la cadena prefijo para nuevos nombres de archivos y directorios.

$ python3 tempfile_settings.py

gettempdir(): /var/folders/5q/8gk0wq888xlggz008k8dr7180000hg/T
gettempprefix(): tmp

El valor devuelto por gettempdir() se establece en función del algoritmo directo de buscar en una lista de ubicaciones el primer lugar donde el proceso actual puede crear un archivo. La lista de búsqueda es:

  1. La variable de entorno TMPDIR
  2. La variable de entorno TEMP
  3. La variable de entorno TMP
  4. Una alternativa, basada en la plataforma. (Windows usa la primera disponible entre C:\temp, C:\tmp, \temp, o \tmp. Otras plataformas usan /tmp, /var/tmp, o /usr/tmp.)
  5. Si no se puede encontrar otro directorio, se utiliza el directorio de trabajo actual.
tempfile_tempdir.py
import tempfile

tempfile.tempdir = '/I/changed/this/path'
print('gettempdir():', tempfile.gettempdir())

Programas que necesitan usar una ubicación global para todos los archivos temporales sin utilizar ninguna de estas variables de entorno deben establecer tempfile.tempdir directamente asignando un valor a la variable.

$ python3 tempfile_tempdir.py

gettempdir(): /I/changed/this/path

Ver también