17 votes

Le multiprocesseur Python : TypeError : attendu chaîne ou objet Unicode, NoneType trouvé

J'essaie de télécharger un répertoire ftp entier en parallèle.

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

Mais je reçois cette exception :

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

Si je commente cette ligne :

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

et supprimez le commentaire sur cette ligne :

downloadFile(filename,local_filename,ftp);

Ensuite, il fonctionne très bien, mais il est lent et n'est pas multithread.

23voto

Multimedia Mike Points 11046

Mise à jour, 9 mai 2014 :

J'ai déterminé la limite précise. Il est possible d'envoyer des objets à travers les frontières de processus vers des processus de travailleur aussi longtemps que les objets peuvent être sélectionnés par La fonction "pickle" de Python . Le problème que j'ai décrit dans ma réponse initiale s'est produit parce que j'essayais d'envoyer un handle de fichier aux workers. Une expérience rapide démontre pourquoi cela ne fonctionne pas :

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

Ainsi, si vous rencontrez l'erreur Python qui vous a amené à trouver cette question sur Stack Overflow, assurez-vous que tous les éléments que vous envoyez au-delà des limites du processus peuvent être décapés.

Réponse originale :

Je suis un peu en retard pour répondre. Cependant, j'ai rencontré le même message d'erreur que l'auteur de l'article original en essayant d'utiliser le module de multitraitement de Python. Je vais enregistrer mes résultats afin que toute personne qui tombe sur ce fil de discussion ait quelque chose à essayer.

Dans mon cas, l'erreur s'est produite à cause de ce que j'essayais d'envoyer au pool de travailleurs : J'essayais de passer un tableau d'objets fichiers pour que les travailleurs du pool puissent le mâcher. C'est apparemment trop pour être envoyé à travers les frontières des processus en Python. J'ai résolu le problème en envoyant aux travailleurs du pool des dictionnaires qui spécifient les chaînes de noms de fichiers d'entrée et de sortie.

Il semble donc que l'itérable que vous fournissez à la fonction, tel que apply_async (J'ai utilisé map() et imap_unordered() ) peut contenir une liste de nombres ou de chaînes de caractères, ou même une structure de données de dictionnaire détaillée (tant que les valeurs ne sont pas des objets).

Dans votre cas :

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

ftp est un objet, qui pourrait être à l'origine du problème. Comme solution de contournement, je recommanderais d'envoyer les paramètres au travailleur (ressemble à host et path dans ce cas) et laisser le travailleur instancier l'objet et s'occuper du nettoyage.

-1voto

ATOzTOA Points 9045

Avez-vous essayé :

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

Le prototype est :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X