¿Cómo crear un archivo temporal que pueda ser leído por un subproceso?


Estoy escribiendo un script Python que necesita escribir algunos datos en un archivo temporal, luego crear un subproceso ejecutando un programa C++ que leerá el archivo temporal. Estoy tratando de usar NamedTemporaryFile para esto, pero según los documentos,

Si el nombre se puede usar para abrir el archivo por segunda vez, mientras el archivo temporal con nombre todavía está abierto, varía entre las plataformas (se puede usar en Unix; no se puede usar en Windows NT o posterior).

Y de hecho, en Windows si Vacio el archivo temporal después de escribir, pero no lo cierro hasta que quiero que desaparezca, el subproceso no es capaz de abrirlo para su lectura.

Estoy trabajando alrededor de esto creando el archivo con delete=False, cerrándolo antes de generar el subproceso, y luego borrándolo manualmente una vez que haya terminado:

fileTemp = tempfile.NamedTemporaryFile(delete = False)
try:
    fileTemp.write(someStuff)
    fileTemp.close()
    # ...run the subprocess and wait for it to complete...
finally:
    os.remove(fileTemp.name)

Esto parece poco elegante. ¿Hay una mejor manera de hacer esto? Tal vez una manera de abrir los permisos en el archivo temporal para que el subproceso puede llegar a él?

Author: Nathan Reed, 2013-03-02

4 answers

Al menos si abre un archivo temporal utilizando bibliotecas Python existentes, no es posible acceder a él desde múltiples procesos en el caso de Windows. De acuerdo con MSDN puede especificar un 3er parámetro (dwSharedMode) bandera de modo compartido FILE_SHARE_READ a la función CreateFile() que:

Habilita operaciones abiertas posteriores en un archivo o dispositivo para solicitar la lectura acceso. De lo contrario, otros procesos no pueden abrir el archivo o dispositivo si solicitan acceso de lectura. Si no se especifica este indicador, pero el file o el dispositivo se ha abierto para el acceso de lectura, la función falla.

Por lo tanto, puede escribir una rutina C específica de Windows para crear una función de abridor de archivos temporal personalizada, llamarla desde Python y luego puede hacer que su subproceso acceda al archivo sin ningún error. Pero creo que debe seguir con su enfoque existente, ya que es la versión más portátil y funcionará en cualquier sistema y, por lo tanto, es la implementación más elegante.

  • Discusión sobre Linux y Windows el bloqueo de archivos se puede encontrar aquí.

EDITAR: Resulta que también es posible abrir y leer el archivo temporal de varios procesos en Windows. Véase la respuesta de Piotr Dobrogost .

 9
Author: Nilanjan Basu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:44

Ya que nadie más parece estar interesado en dejar esta información al descubierto...

tempfile expone una función, mkdtemp(), que puede trivializar este problema:

try:
    temp_dir = mkdtemp()
    temp_file = make_a_file_in_a_dir(temp_dir)
    do_your_subprocess_stuff(temp_file)
    remove_your_temp_file(temp_file)
finally:
    os.rmdir(temp_dir)

Dejo la implementación de las funciones intermedias en manos del lector, ya que uno podría desear hacer cosas como usar mkstemp() para reforzar la seguridad del archivo temporal en sí, o sobrescribir el archivo en su lugar antes de eliminarlo. No sé en particular qué restricciones de seguridad uno podría tener que no se planifican fácilmente mediante el examen de la fuente de tempfile.

De todos modos, sí, usar NamedTemporaryFile en Windows podría ser poco elegante, y mi solución aquí también podría ser poco elegante, pero ya ha decidido que el soporte de Windows es más importante que el código elegante, por lo que también podría seguir adelante y hacer algo legible.

 25
Author: Corbin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-06 22:27:25

Según[10]} Richard Oudkerk

(...) la única razón por la que intentar reabrir un NamedTemporaryFile falla Windows es porque cuando reabrimos necesitamos usar O_TEMPORARY.

Y él da un ejemplo de cómo hacer esto en Python 3.3+

import os, tempfile

DATA = b"hello bob"

def temp_opener(name, flag, mode=0o777):
    return os.open(name, flag | os.O_TEMPORARY, mode)

with tempfile.NamedTemporaryFile() as f:
    f.write(DATA)
    f.flush()
    with open(f.name, "rb", opener=temp_opener) as f:
        assert f.read() == DATA

assert not os.path.exists(f.name)

Porque no hay un parámetro opener en el open() integrado en Python 2.x, tenemos que combinar las funciones de nivel inferior os.open() y os.fdopen() para lograr el mismo efecto:

import subprocess
import tempfile

DATA = b"hello bob"

with tempfile.NamedTemporaryFile() as f:
    f.write(DATA)
    f.flush()

    subprocess_code = \
    """import os
       f = os.fdopen(os.open(r'{FILENAME}', os.O_RDWR | os.O_BINARY | os.O_TEMPORARY), 'rb')
       assert f.read() == b'{DATA}'
    """.replace('\n', ';').format(FILENAME=f.name, DATA=DATA)

    subprocess.check_output(['python', '-c', subprocess_code]) == DATA
 21
Author: Piotr Dobrogost,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-12 22:51:25

Siempre puedes ir de bajo nivel, aunque no estoy seguro de si es lo suficientemente limpio para ti:

fd, filename = tempfile.mkstemp()
try:
    os.write(fd, someStuff)
    os.close(fd)
    # ...run the subprocess and wait for it to complete...
finally:
    os.remove(filename)
 11
Author: Tshepang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-02 01:04:53