Подтвердить что ты не робот

Многопроцессорность Python: TypeError: ожидаемая строка или объект Unicode, обнаружен NoneType

Я пытаюсь загрузить всю директорию ftp параллельно.

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

Но я получаю это исключение:

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

Если я прокомментирую эту строку:

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

и удалите комментарий по этой строке:

downloadFile(filename,local_filename,ftp);

Затем он работает просто отлично, но он медленный, а не многопоточный.

4b9b3361

Ответ 1

Обновление, 9 мая 2014 года:

Я определил точное ограничение. Можно отправлять объекты через границы процесса рабочим процессам, пока объекты могут быть маринованными Python pickle facility. Проблема, которую я описал в моем первоначальном ответе, возникла из-за того, что я пытался отправить дескриптор файла рабочим. Быстрый эксперимент показывает, почему это не работает:

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

Таким образом, если вы столкнулись с ошибкой Python, которая заставила вас найти этот вопрос, убедитесь, что все вещи, которые вы отправляете через границы процесса, можно мариновать.

Оригинальный ответ:

Я немного опоздал, чтобы ответить. Тем не менее, я столкнулся с тем же сообщением об ошибке, что и исходный плакат, пытаясь использовать модуль многопроцессорности Python. Я запишу свои выводы, чтобы кто-нибудь, кто наткнулся на эту тему, попытался попробовать.

В моем случае ошибка произошла из-за того, что я пытался отправить в пул работников: я пытался передать массив файловых объектов для работников пула, чтобы пережевывать. Это, видимо, слишком много для отправки через границы процесса в Python. Я решил проблему, отправив словари пула работников, которые указали строки ввода и вывода имени файла.

Таким образом, кажется, что итерабельность, которую вы передаете функции, такой как apply_async (я использовал map() и imap_unordered()), может содержать список чисел или строк или даже подробную структуру данных словаря (как долго поскольку значения не являются объектами).

В вашем случае:

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

ftp - это объект, который может вызвать проблему. В качестве обходного пути я бы рекомендовал отправить параметры рабочему (в этом случае выглядит как host и path), и пусть работник создает экземпляр объекта и обрабатывает очистку.

Ответ 2

Вы пробовали:

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

Прототип:

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