web-dev-qa-db-fra.com

Comment créer un fichier temporaire lisible par un sous-processus?

J'écris un script Python qui doit écrire des données dans un fichier temporaire, puis créer un sous-processus exécutant un programme C++ qui lira le fichier temporaire. J'essaie d'utiliser - NamedTemporaryFile pour cela, mais selon les documents,

Si le nom peut être utilisé pour ouvrir le fichier une deuxième fois, alors que le fichier temporaire nommé est toujours ouvert, cela varie selon les plateformes (il peut être utilisé sur Unix; il ne peut pas sur Windows NT ou version ultérieure).

Et en effet, sous Windows, si je vide le fichier temporaire après l'écriture, mais ne le fermez pas tant que je ne veux pas qu'il disparaisse, le sous-processus ne peut pas l'ouvrir en lecture.

Je contourne ce problème en créant le fichier avec delete=False, en le fermant avant de lancer le sous-processus, puis en le supprimant manuellement une fois que j'ai terminé:

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)

Cela semble inélégant. Y a-t-il une meilleure manière de faire cela? Peut-être un moyen d'ouvrir les autorisations sur le fichier temporaire afin que le sous-processus puisse y accéder?

45
Nathan Reed

Au moins, si vous ouvrez un fichier temporaire à l'aide des bibliothèques Python existantes, il n'est pas possible d'y accéder à partir de plusieurs processus sous Windows. Selon MSDN vous pouvez spécifier un 3ème paramètre (dwSharedMode) indicateur de mode partagé FILE_SHARE_READ à CreateFile() fonction qui:

Permet aux opérations d'ouverture suivantes sur un fichier ou un périphérique de demander un accès en lecture. Sinon, d'autres processus ne peuvent pas ouvrir le fichier ou le périphérique s'ils demandent un accès en lecture. Si cet indicateur n'est pas spécifié, mais que le fichier ou le périphérique a été ouvert pour un accès en lecture, la fonction échoue.

Ainsi, vous pouvez écrire une routine C spécifique à Windows pour créer une fonction d'ouverture de fichier temporaire personnalisée, l'appeler à partir de Python puis vous pouvez faire en sorte que votre sous-processus accède au fichier sans aucune erreur. Mais je pensez que vous devez vous en tenir à votre approche existante car c'est la version la plus portable et fonctionnera sur n'importe quel système et est donc la mise en œuvre la plus élégante.

  • Une discussion sur le verrouillage de fichiers Linux et Windows peut être trouvée ici .

EDIT: Il s'avère qu'il est également possible d'ouvrir et de lire le fichier temporaire à partir de plusieurs processus dans Windows. Voir Piotr Dobrogost réponse .

9
Nilanjan Basu

Étant donné que personne d'autre ne semble intéressé à laisser ces informations à l'air libre ...

tempfile expose une fonction, mkdtemp(), ce qui peut banaliser ce problème:

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)

Je laisse l'implémentation des fonctions intermédiaires au lecteur, car on pourrait souhaiter faire des choses comme utiliser mkstemp() pour renforcer la sécurité du fichier temporaire lui-même, ou écraser le fichier en place avant de le supprimer . Je ne sais pas particulièrement quelles restrictions de sécurité on pourrait avoir qui ne sont pas faciles à planifier en parcourant la source de tempfile.

Quoi qu'il en soit, oui, l'utilisation de NamedTemporaryFile sur Windows peut être inélégante, et ma solution ici peut également être inélégante, mais vous avez déjà décidé que la prise en charge de Windows est plus importante que le code élégant, vous pouvez donc aussi bien aller de l'avant et faire quelque chose de lisible.

24
Corbin

Selon à Richard Oudkerk

(...) la seule raison pour laquelle la tentative de réouverture d'un NamedTemporaryFile échoue sous Windows est que lorsque nous rouvrons, nous devons utiliser O_TEMPORARY.

et il donne un exemple de la façon de le faire dans 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)

Parce qu'il n'y a pas de paramètre opener dans le open() intégré dans Python 2.x, nous devons combiner le niveau inférieur os.open() et os.fdopen() fonctionne pour obtenir le même effet:

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
22
Piotr Dobrogost

Vous pouvez toujours aller à bas niveau, mais je ne sais pas si c'est assez propre pour vous:

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)
12
Tshepang