2012-08-09 2 views
32

Ho un codice legacy con una funzione legacy che accetta un nome file come argomento e elabora il contenuto del file. Di seguito è riportato un facsimile di lavoro del codice.StringIO e compatibilità con l'istruzione 'with' (gestore contesto)

Quello che voglio fare non è scrivere sul disco con del contenuto che generi per utilizzare questa funzione legacy, quindi potrei usare StringIO per creare un oggetto al posto del nome file fisico. Tuttavia, questo non funziona, come puoi vedere qui sotto.

Pensavo che il StringIO fosse la strada da seguire. Qualcuno può dirmi se c'è un modo per usare questa funzione legacy e passarla qualcosa nell'argomento che non è un file su disco ma può essere trattato come tale dalla funzione legacy? La funzione legacy ha il gestore contesto with che lavora sul valore del parametro filename.

L'unica cosa che mi sono imbattuto in Google è stato: http://bugs.python.org/issue1286, ma questo non mi ha aiutato ...

Codice

from pprint import pprint 
import StringIO 

    # Legacy Function 
def processFile(filename): 
    with open(filename, 'r') as fh: 
     return fh.readlines() 

    # This works 
print 'This is the output of FileOnDisk.txt' 
pprint(processFile('c:/temp/FileOnDisk.txt')) 
print 

    # This fails 
plink_data = StringIO.StringIO('StringIO data.') 
print 'This is the error.' 
pprint(processFile(plink_data)) 

uscita

Questa è la uscita in FileOnDisk.txt:

['This file is on disk.\n'] 

Questo è l'errore:

Traceback (most recent call last): 
    File "C:\temp\test.py", line 20, in <module> 
    pprint(processFile(plink_data)) 
    File "C:\temp\test.py", line 6, in processFile 
    with open(filename, 'r') as fh: 
TypeError: coercing to Unicode: need string or buffer, instance found 
+3

cant "Apri" un'istanza StringIO –

risposta

52

Un'istanza StringIOè già aperto. Il comando open, d'altra parte, accetta solo i nomi di file, per restituire un file aperto. Un'istanza StringIO non è idonea come nome file.

Inoltre, non è necessario chiudere un'istanza StringIO, quindi non è necessario utilizzarlo come gestore di contesto.

Se tutto il codice precedente può richiedere un nome file, l'istanza StringIO non è la soluzione giusta. Utilizzare lo tempfile module per generare invece un nome file temporaneo.

Ecco un esempio utilizzando un contextmanager per garantire il file temporaneo viene ripulito in seguito:

import os 
import tempfile 
from contextlib import contextmanager 

@contextmanager 
def tempinput(data): 
    temp = tempfile.NamedTemporaryFile(delete=False) 
    temp.write(data) 
    temp.close() 
    try: 
     yield temp.name 
    finally: 
     os.unlink(temp.name) 

with tempinput('Some data.\nSome more data.') as tempfilename: 
    processFile(tempfilename) 
+0

Sto usando questa soluzione. Ecco un link al codice di esempio che implementa questo direttamente: http://pastie.org/4450354. Grazie a tutti quelli che hanno contribuito qui! – mpettis

+2

@mpettis: Ho aggiornato la mia risposta per dare un esempio usando un gestore di contesto che creerà il file temporaneo e lo pulirà per voi in una volta. –

+0

Questo è davvero un modo elegante per gestire questo ... Grazie! – mpettis

4

si potrebbe definire una propria funzione open

fopen = open 
def open(fname,mode): 
    if hasattr(fname,"readlines"): return fname 
    else: return fopen(fname,mode) 

ma con voglia di chiamare __exit__ dopo il suo fare e StringIO non dispone di un metodo di uscita ...

si potrebbe definire una classe personalizzata da utilizzare con questo aperto

class MyStringIO: 
    def __init__(self,txt): 
     self.text = txt 
    def readlines(self): 
      return self.text.splitlines() 
    def __exit__(self): 
      pass 
+0

Purtroppo questo non risolve il problema in quanto avrebbe dovuto essere all'interno della funzione legacy – jdi

+0

questo non lo sovrascriverà fino a quando si trovava nello stesso file? –

+0

@jdi Penso che potrebbe funzionare se fosse stato definito prima della funzione legacy, cioè quando viene importato il modulo legacy. –