2013-01-08 11 views
13

Próbuję pobrać cały katalog ftp równolegle.Wieloprocesorowe przetwarzanie w języku Python: TypeError: oczekiwany ciąg znaków lub obiekt Unicode, znaleziono NoneType

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

Ale otrzymuję ten wyjątek:

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 

Gdybym skomentować tę linię:

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

i usunąć komentarz na tej linii:

downloadFile(filename,local_filename,ftp); 

Następnie działa dobrze, ale jest powolny, a nie wielowątkowy.

+0

Ten kod nie działa tak, jak został zapisany (są błędy nazw zmiennych w pliku 'downloadFiles'). Czy możesz opublikować działający kod i pokazać przykładowe wywołanie 'downloadFiles', które demonstruje problem? –

+0

W porządku - poprawiłem mój post. –

+0

try: 'from multiprocessing.dummy importuj Pool', który używa wątków zamiast procesów jako szybkie obejście, jeśli działa, wtedy problem może polegać na inicjalizacji/przekazywaniu niektórych obiektów np.' Ftp' do procesów potomnych. Zawiń 'downloadFile()' body w bloku 'try/except', aby logować wyjątki w przypadku, gdy ta wersja' wieloprocesowości' niepoprawnie je zgłosi. – jfs

Odpowiedz

-1

Czy próbowałeś:

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

Prototyp jest:

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

Update, 9 maja 2014 roku:

I ustalili dokładną ograniczenie. Możliwe jest wysyłanie obiektów pomiędzy granicami procesu do procesów roboczych, o ile obiekty mogą być kiszone przez Python's pickle facility. Problem, który opisałem w mojej oryginalnej odpowiedzi, wystąpił, ponieważ próbowałem wysłać uchwyt pliku do pracowników. Szybkie eksperyment pokazuje, dlaczego to nie działa:

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

Tak więc, jeśli napotyka błąd Pythona, która doprowadziła do odnalezienia na to pytanie przepełnieniem stosu, upewnij się, że wszystkie rzeczy, które wysyłasz przez granice procesu można marynować.

Oryginalny odpowiedź:

jestem trochę późno, aby odpowiadając. Jednak natrafiłem na ten sam komunikat o błędzie, co oryginalny plakat, próbując użyć modułu wieloprocesorowego Pythona. Będę nagrywał moje odkrycia, aby każdy, kto natknął się na ten wątek, miał coś do spróbowania.

W moim przypadku błąd wystąpił z powodu tego, co próbowałem wysłać do puli pracowników: Próbowałem przekazać tablicę obiektów plików, aby pracownicy basenu mogli je przeżuwać. To najwyraźniej za dużo, aby przesłać granice procesów w Pythonie. Rozwiązałem problem, wysyłając słowniki pracowników basenów, które określały wejściowe i wyjściowe ciągi nazw plików.

Wygląda więc na to, że iterable, które dostarczają funkcji, takich jak apply_async (użyłem map() i imap_unordered()) może zawierać listę liczb lub ciągów, a nawet szczegółowej struktury danych słownika (o ile wartości aren” t obiektów).

W twoim przypadku:

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

ftp jest obiektem, który może być przyczyną problemu. W celu obejścia tego problemu zaleca się wysłanie parametrów do procesu roboczego (w tym przypadku wygląda na to, że są to: host i path), a pracownik powinien utworzyć instancję obiektu i postępować zgodnie z procedurą czyszczenia.

+0

Poważnie nie możesz odwzorować obiektów na funkcje z obsługą wieloprocesową? – CornSmith

+0

Nie jestem pewien, jakie są dokładne ograniczenia. To, co opisałem, rozwiązało mój problem. –

+0

Tak, musiałem wykonać pracę tak samo jak ty (tylko skończyłem używając wątków zamiast). Czy to ograniczenie wynika z GIL? Zastanawiam się, czy Python był bezpieczny dla wątków, mógł to zrobić. – CornSmith

Powiązane problemy