2013-01-08 15 views
13

Ich versuche, ein ganzes FTP-Verzeichnis parallel zu laden.Python Multiprocessing: TypeError: erwartete Zeichenfolge oder Unicode-Objekt, NoneType gefunden

#!/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() 

Aber ich bekomme diese Ausnahme:

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 

Wenn ich diese Zeile aus kommentieren:

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

und entfernen Sie den Kommentar zu dieser Zeile:

downloadFile(filename,local_filename,ftp); 

Dann es funktioniert gut, aber es ist langsam und nicht Multithread.

+0

Dieser Code läuft nicht wie geschrieben (es gibt Variablennamenfehler in 'downloadFiles'). Können Sie Arbeitscode posten und einen Beispielaufruf von 'downloadFiles' zeigen, der das Problem demonstriert? –

+0

In Ordnung - ich habe meinen Beitrag überarbeitet. –

+0

versuchen: 'aus multiprocessing.dummy Import Pool ', die Threads anstelle von Prozessen als schnelle Abhilfe verwendet, wenn es funktioniert, dann könnte das Problem in der Initialisierung sein/einige Objekte wie ftp an untergeordnete Prozesse übergeben. Wickeln Sie 'downloadFile()' body in 'try/except' Block, um Ausnahmen zu protokollieren, falls diese Version von' multiprocessing' diese fälschlicherweise meldet. – jfs

Antwort

-1

Haben Sie versucht:

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

Der Prototyp ist:

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

aktualisieren, 9. Mai 2014:

ich die genaue Begrenzung bestimmt haben. Es ist möglich, Objekte über Prozessgrenzen hinweg an Worker-Prozesse zu senden, solange die Objekte von Python's pickle facility gebeizt werden können. Das Problem, das ich in meiner ursprünglichen Antwort beschrieben habe, ist aufgetreten, weil ich versucht habe, ein Dateihandle an die Arbeiter zu senden. Ein kurzer Versuch zeigt, warum dies nicht funktioniert:

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

Wenn Sie also den Python-Fehler sind zu begegnen, die Sie führte diese Stack-Überlauf Frage zu finden, stellen Sie sicher, dass alle Dinge, die Sie über Prozessgrenzen hinweg sind das Senden kann gebeizt werden.

Ursprüngliche Antwort:

Ich bin ein bisschen spät zu beantworten. Beim Versuch, Pythons Multiprocessing-Modul zu verwenden, trat jedoch die gleiche Fehlermeldung auf wie beim ursprünglichen Poster. Ich werde meine Ergebnisse aufzeichnen, so dass jeder, der auf diesen Thread stolpert, etwas ausprobieren muss.

In meinem Fall trat der Fehler aufgrund dessen auf, was ich versuchte, an den Pool von Arbeitern zu senden: Ich habe versucht, ein Array von Dateiobjekten für die Pool-Arbeiter zu übergeben, um zu kauen. Das ist offensichtlich zu viel, um in Python Prozessgrenzen zu überschreiten. Ich löste das Problem, indem ich die Pool Worker-Wörterbücher sendete, die Eingabe- und Ausgabedateinamen-Zeichenfolgen spezifizierten.

So scheint es, dass der iterable, die Sie an die Funktion liefern wie apply_async (I verwendet map() und imap_unordered()) eine Liste von Zahlen oder Strings oder sogar eine detaillierten Wörterbuch Datenstruktur enthalten kann (solange die Werte aren‘ t Objekte).

In Ihrem Fall:

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

ftp ist eine Aufgabe, die das Problem verursachen könnte. Als Workaround empfehle ich, die Parameter an den Worker zu senden (sieht in diesem Fall aus wie host und path) und lässt den Worker das Objekt instanziieren und mit der Bereinigung befassen.

+0

Sie können Objekte nicht wirklich Funktionen mit Multiprocessing zuordnen? – CornSmith

+0

Ich bin nicht sicher, was die genauen Beschränkungen sind. Was ich beschrieben habe, hat mein Problem gelöst. –

+0

Ja, ich musste auch so arbeiten wie du (nur am Ende habe ich Threads benutzt). Ist diese Einschränkung auf die GIL zurückzuführen? Ich denke, wenn Python Thread-sicher wäre, könnte es das tun. – CornSmith

Verwandte Themen