2013-03-02 12 views
36

Sto scrivendo uno script Python che deve scrivere alcuni dati in un file temporaneo, quindi creare un sottoprocesso che esegue un programma C++ che leggerà il file temporaneo. Sto cercando di utilizzare NamedTemporaryFile per questo, ma secondo la documentazione,Come creare un file temporaneo che può essere letto da un sottoprocesso?

Se il nome può essere utilizzato per aprire il file una seconda volta, mentre il file temporaneo denominato è ancora aperto, varia tra le piattaforme (che può essere usato su Unix, ma non su Windows NT o successivi).

E in effetti, in Windows se scarico il file temporaneo dopo la scrittura, ma non lo chiudo finché non lo voglio, il sottoprocesso non è in grado di aprirlo per la lettura.

sto lavorando intorno a questo creando il file con delete=False, chiudendola prima di deposizione delle uova il sottoprocesso, e quindi manualmente l'eliminazione di una volta ho finito:

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) 

Questo sembra poco elegante. C'è un modo migliore per farlo? Forse un modo per aprire le autorizzazioni sul file temporaneo in modo che il sottoprocesso possa arrivare a questo?

+0

Avete considerato la possibilità di convogliare i processi come una possibilità? –

+0

@JonClements Sì, anche le piping sono possibili, ma richiederebbe una modifica all'interfaccia del sottoprocesso, che spero di evitare. –

+1

Correlati: Problema Python 14243 - [tempfile.NamedTemporaryFile non particolarmente utile su Windows] (http://bugs.python.org/issue14243) –

risposta

9

Almeno se si apre un file temporaneo utilizzando le librerie Python esistenti, l'accesso da più processi, non è possibile in caso di Windows. Secondo MSDN è possibile specificare un terzo parametro (dwSharedMode) bandiera modalità condivisa FILE_SHARE_READ-CreateFile() funzione che:

Attiva le successive operazioni di apertura su un file o un dispositivo di richiesta di lettura accesso. In caso contrario, altri processi non possono aprire il file o il dispositivo se richiedono l'accesso in lettura. Se questo flag non è specificato, ma il file o il dispositivo è stato aperto per l'accesso in lettura, la funzione non riesce.

Quindi, è possibile scrivere una routine C specifica di Windows per creare una funzione di apertura file temporanea personalizzata, chiamarla da Python e quindi è possibile fare in modo che il sottoprocesso acceda al file senza errori. Ma penso che dovresti seguire il tuo approccio esistente in quanto è la versione più portatile e funzionerà su qualsiasi sistema e quindi è l'implementazione più elegante.

  • Discussione su Linux e il blocco del file di Windows può essere trovato here.

MODIFICA: risulta che è possibile aprire & leggere il file temporaneo da più processi anche in Windows. Vedi Piotr Dobrogost's answer.

+0

Grazie. Vorrei che l'API Python esponesse quei flag di modalità ma immagino di poter capire perché non lo fanno. Oh bene. –

+0

* Almeno se apri un file temporaneo utilizzando le librerie Python esistenti, non è possibile accedervi da più processi in caso di Windows. * Non è vero. Vedi http://bugs.python.org/issue14243#msg164504 e la mia [risposta] (http://stackoverflow.com/a/15235559/95735). –

12

si può sempre andare a basso livello, anche se non sono sicuro se è abbastanza pulito per voi:

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) 
19

According a Richard Oudkerk

(...) l'unico motivo che il tentativo di riaprire un NamedTemporaryFile fallisce su Windows è perché quando si riprendono abbiamo bisogno di usare O_TEMPORARY.

e dà un esempio di come fare questo in 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) 

Perché non c'è parametro opener nel built-in open() in Python 2.x, dobbiamo combinare livello più basso os.open() e os.fdopen() funzioni per ottenere lo stesso effetto:

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 
+0

Interessante ... Sono in Python 2.x ma dovrò approfondirlo ulteriormente. –

+0

@NathanReed È anche possibile in Python 2.x - vedere il mio aggiornamento. –

+1

Si noti che questo funziona solo se si controlla il codice nel sottoprocesso. Chiamare un'utilità di sistema come 'tar' non funzionerà banalmente. Ancora, trovare pulito! –

24

Poiché nessuno sembra essere interessato a lasciare queste informazioni alla luce del sole ...

tempfile fa esporre una funzione, mkdtemp(), che può banalizzare questo 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) 

lascio l'attuazione delle funzioni intermedie fino al lettore, come si potrebbe desiderare di fare le cose come l'uso mkstemp() per aumentare la sicurezza del file temporaneo stesso o sovrascrivere il file sul posto prima di rimuoverlo. Non so in particolare quali restrizioni di sicurezza si possano avere e che non siano facilmente pianificate esaminando la fonte di tempfile.

In ogni caso, si, usare NamedTemporaryFile su Windows potrebbe essere inelegante, e la mia soluzione qui potrebbe anche essere poco elegante, ma hai già deciso che il supporto di Windows è più importante di un codice elegante, quindi potresti anche andare avanti e fare qualcosa di leggibile.

+3

Il codice cleanup può essere semplificato con 'shutil.rmtree (temp_dir)' –

+0

Potrei sbagliarmi, ma questa sembra la migliore risposta. –

+2

Attenzione! ** Questo non risponde affatto alla domanda originale. ** Non solo questo non è migliore di quello che OP sta già facendo, ma è molto peggio lasciare gli aspetti fragili della creazione di file temporanei all'utente ''make_a_file_in_a_dir (temp_dir)'. Si prega di vedere la mia risposta per un modo molto migliore per gestire questo.(Non capisco perché non abbia scritto questo commento quattro anni prima, quando ho letto per la prima volta questo ...). –

Problemi correlati