2013-01-08 3 views
13

Sto tentando di scaricare un'intera directory ftp in parallelo.Multiprocessing di Python: TypeError: stringa attesa o oggetto Unicode, NoneType found

#!/usr/bin/python 
import sys 
import datetime 
import os 
from multiprocessing import Process, Pool 
from ftplib import FTP 
curYear="" 
remotePath ="" 
localPath = "" 

def downloadFiles (remotePath,localPath): 
     splitted = remotePath.split('/'); 
     host= splitted[2] 
     path='/'+'/'.join(splitted[3:]) 
     ftp = FTP(host) 
     ftp.login() 
     ftp.cwd(path) 
     filenames = ftp.nlst() 
     total=len(filenames) 
     i=0 
     pool = Pool() 
     for filename in filenames: 
         local_filename = os.path.join(localPath,filename) 
         pool.apply_async(downloadFile, (filename,local_filename,ftp)) 
         #downloadFile(filename,local_filename,ftp); 
         i=i+1 

     pool.close() 
     pool.join() 
     ftp.close() 

def downloadFile(filename,local_filename,ftp): 
     file = open(local_filename, 'wb') 
     ftp.retrbinary('RETR '+ filename, file.write) 
     file.close() 

def getYearFromArgs(): 
     if len(sys.argv) >= 2 and sys.argv[1] == "Y": 
       year = sys.argv[2] 
       del sys.argv[1:2] 
     else: 
       year = str(datetime.datetime.now().year) 
     return year 

def assignGlobals(): 
     global p 
     global remotePath 
     global localPath 
     global URL 
     global host 
     global user 
     global password 
     global sqldb 
     remotePath = 'ftp://ftp3.ncdc.noaa.gov/pub/data/noaa/isd-lite/%s/' % (curYear) 
     localPath = '/home/isd-lite/%s/' % (curYear) 

def main(): 
     global curYear 
     curYear=getYearFromArgs() 
     assignGlobals() 
     downloadFiles(remotePath,localPath) 

if __name__ == "__main__": 
     main() 

ma ottengo questa eccezione:

Exception in thread Thread-1: 
Traceback (most recent call last): 
    File "/usr/lib64/python2.6/threading.py", line 532, in __bootstrap_inner 
    self.run() 
    File "/usr/lib64/python2.6/threading.py", line 484, in run 
    self.__target(*self.__args, **self.__kwargs) 
    File "/usr/lib64/python2.6/multiprocessing/pool.py", line 225, in _handle_tasks 
    put(task) 
TypeError: expected string or Unicode object, NoneType found 

Se io commento questa riga:

pool.apply_async(downloadFile, (filename,local_filename,ftp) 

e rimuovere il commento su questa linea:

downloadFile(filename,local_filename,ftp); 

Poi funziona bene ma è lento e non lo è multithread.

+0

Questo codice non viene eseguito come scritto (ci sono errori di nome variabile in 'downloadFiles'). Puoi pubblicare codice funzionante e mostrare una chiamata di esempio di 'downloadFiles' che dimostra il problema? –

+0

OK - Ho modificato il mio post. –

+0

provare: 'da multiprocessing.dummy import Pool' che utilizza thread anziché processi come soluzione rapida se funziona, il problema potrebbe essere nell'inizializzazione/passaggio di alcuni oggetti, ad esempio' ftp' nei processi figlio. Avvolgi il corpo 'downloadFile()' nel blocco 'try/except' per registrare le eccezioni nel caso in cui questa versione di' multiprocessing' le indichi erroneamente. – jfs

risposta

-1

Hai provato:

pool.apply_async(downloadFile, args=(filename,local_filename,ftp)) 

Il prototipo è:

apply_async(func, args=(), kwds={}, callback=None) 
18

aggiornamento, 9 maggio 2014:

ho determinato la limitazione precisa. È possibile inviare oggetti oltre i limiti del processo a processi di lavoro purché gli oggetti possano essere decapitati da Python's pickle facility. Il problema che ho descritto nella mia risposta originale si è verificato perché stavo cercando di inviare un handle di file ai lavoratori. Un esperimento rapido dimostra perché questo non funziona:

>>> f = open("/dev/null") 
>>> import pickle 
>>> pickle.dumps(f) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/usr/lib/python2.7/pickle.py", line 1374, in dumps 
    Pickler(file, protocol).dump(obj) 
    File "/usr/lib/python2.7/pickle.py", line 224, in dump 
    self.save(obj) 
    File "/usr/lib/python2.7/pickle.py", line 306, in save 
    rv = reduce(self.proto) 
    File "/usr/lib/python2.7/copy_reg.py", line 70, in _reduce_ex 
    raise TypeError, "can't pickle %s objects" % base.__name__ 
TypeError: can't pickle file objects 

Quindi, se si sta incontrando l'errore di Python, che ti ha portato a trovare questa domanda Stack Overflow, assicurarsi che tutte le cose che si sta inviando attraverso i limiti del processo può essere messo in salamoia

risposta originale:

io sono un po 'tardi per rispondere. Tuttavia, mi sono imbattuto nello stesso messaggio di errore del poster originale durante il tentativo di utilizzare il modulo di multiprocessing di Python. Registrerò i miei risultati in modo che chiunque altro incappi in questo thread abbia qualcosa da provare.

Nel mio caso, l'errore si è verificato a causa di ciò che stavo cercando di inviare al pool di lavoratori: stavo cercando di passare una matrice di oggetti file per i lavoratori della piscina da masticare. È apparentemente troppo da inviare oltre i limiti del processo in Python. Ho risolto il problema inviando i dizionari dei pool worker che hanno specificato stringhe di nome file di input e di output.

così sembra che l'iterabile che si fornisce alla funzione, come apply_async (ho usato map() e imap_unordered()) può contenere un elenco di numeri o stringhe, o anche una dettagliata struttura di dati dizionario (a patto che i valori aren' t oggetti).

Nel tuo caso:

pool.apply_async(downloadFile, (filename,local_filename,ftp)) 

ftp è un oggetto, che potrebbe essere la causa del problema. Come soluzione alternativa, consiglierei di inviare i parametri all'operatore (sembra host e path in questo caso) e lasciare che il lavoratore istanzia l'oggetto e gestisca la pulizia.

+0

È seriamente impossibile mappare oggetti alle funzioni con multiprocessing? – CornSmith

+0

Non sono sicuro di quali siano i limiti esatti. Quello che ho descritto è ciò che ha risolto il mio problema. –

+0

Sì, ho dovuto fare un lavoro anche io come te (solo io ho finito per usare i thread). Questa limitazione è dovuta al GIL? Sto pensando che se Python fosse sicuro, potrebbe farlo. – CornSmith