2 votes

En utilisant la multiprogrammation Python tout en important un module via le chemin du fichier

Je suis en train d'écrire un programme qui importe un module en utilisant un chemin de fichier, avec la fonction imp.load_source(nom_module, chemin_module). Cela semble poser problème lorsque j'essaie de passer des objets de ce module dans un Process.

Un exemple:

import multiprocessing
import imp

class MyProcess(multiprocessing.Process):
    def __init__(self,chose):
        multiprocessing.Process.__init__(self)
        self.chose=chose
    def run(self):
        x=self.chose

if __name__=="__main__":
    module=imp.load_source('life', 'C:\\Documents and Settings\\User\\workspace\\GarlicSim\\src\\simulations\\life\\life.py')
    chose=module.step
    print(chose)
    p=MyProcess(chose)
    p.start()

Note: pour que ce code "fonctionne", vous devez substituer les paramètres que j'ai donnés à imp.load_source par quelque chose d'autre : Il doit s'agir d'un fichier Python sur votre ordinateur, de préférence pas dans le même dossier. Ensuite, dans chose=module.step, au lieu de step, mettez une fonction ou une classe aléatoire qui est définie dans ce fichier .py.

Je reçois la trace suivante:

Traceback (most recent call last):
  File "", line 1, in 
  File "C:\Python26\lib\multiprocessing\forking.py", line 342, in main
    self = load(from_parent)
  File "C:\Python26\lib\pickle.py", line 1370, in load
    return Unpickler(file).load()
  File "C:\Python26\lib\pickle.py", line 858, in load
    dispatch[key](self)
  File "C:\Python26\lib\pickle.py", line 1090, in load_global
    klass = self.find_class(module, name)
  File "C:\Python26\lib\pickle.py", line 1124, in find_class
    __import__(module)
ImportError: No module named life

Que dois-je faire ?

EDIT:

Je suis en train d'utiliser Python 2.6.2c1 sur Win XP.

1voto

Konstantin Tenzin Points 3929

Probablement cela ne fonctionne pas en raison du placement du code d'importation dans le bloc principal. Le code ci-dessous fonctionne sous Windows XP, Python 2.6. Ensuite, le module life sera également importé dans un nouveau processus.

import multiprocessing
import imp

class MyProcess(multiprocessing.Process):
  def __init__(self,thing):
    multiprocessing.Process.__init__(self)
    self.thing=thing
  def run(self):
    print("En train de quitter self.thing")
    self.thing()
    print("Terminé")

life=imp.load_source('life', r'd:\temp5\life.py')

if __name__=="__main__":
  p=MyProcess(life.step)
  p.start()

-1voto

nosklo Points 75862

test.py sur n'importe quel dossier:

import multiprocessing
import imp

class MyProcess(multiprocessing.Process):
    def __init__(self,thing):
        multiprocessing.Process.__init__(self)
        self.thing=thing
    def run(self):
        print 'running...', self.thing()

if __name__=="__main__":
    module=imp.load_source('life', '/tmp/life.py')
    thing=module.step
    print(thing)
    p=MyProcess(thing)
    p.start()

life.py sur /tmp:

def step():
    return 'Ça marche!'

Exécution de test.py:

$ python test.py

running... Ça marche!

Je viens de tester et ça marche, donc vous devez faire quelque chose d'autre de mal. Veuillez modifier votre question et coller le code réel qui ne fonctionne pas.

J'utilise Ubuntu Jaunty 9.04 avec python par défaut (Python 2.6.2 release26-maint, Apr 19 2009, 01:56:41). Je ne sais pas si votre problème est spécifique à Windows, car je n'ai pas de Windows disponible pour tester.

C'est une raison pour laquelle manipuler les chemins d'importation est une mauvaise idée. Vous ne les obtiendrez jamais correctement pour toutes les plateformes/environnements, et vos utilisateurs seront probablement mécontents. Il vaut mieux simplement utiliser la manière python de trouver les modules, qui consiste à placer le module sur le chemin de recherche des modules python. Cela fonctionnerait de façon cohérente partout.

-1voto

Jon Cage Points 14126

J'ai juste fait ce qui suit, en utilisant XP avec Python 2.5...

D:\Experiments\ModuleLoading\test.py

import imp

if __name__=="__main__":
    module=imp.load_source('life', r'D:\Experiments\ModuleLoading\somefolder\life.py')
    thing=module.step
    print(thing)

D:\Experiments\ModuleLoading\somefolder\step.py

def step():
    return 'Cela fonctionne!'

...et l'exécution du script donne :

D:\jcage\Projects\Experiments\ModuleLoading>test.py

...donc essayez ça d'abord et assurez-vous que le module peut être chargé sans multiprocessing?

[Edit] D'accord, donc c'est définitivement un problème lors de l'importation dans le processus forké. Il y a quelques éléments dans la documentation qui sont spécifiques à Windows:

Plus de picklabilité

Assurez-vous que tous les arguments de Process.__init__() sont picklable. Cela signifie, en particulier, que les méthodes liées ou non liées ne peuvent pas être utilisées directement comme argument cible sur Windows - il suffit de définir une fonction et de l'utiliser à la place. En outre, si vous sous-classez Process, assurez-vous que les instances seront picklables lorsque la méthode Process.start() est appelée.

Variables globales

Gardez à l'esprit que si le code exécuté dans un processus enfant tente d'accéder à une variable globale, alors la valeur qu'il voit (le cas échéant) peut ne pas être la même que la valeur dans le processus parent au moment où Process.start() a été appelé. Cependant, les variables globales qui ne sont que des constantes au niveau du module ne posent aucun problème.

Importation sûre du module principal

Assurez-vous que le module principal peut être importé en toute sécurité par un nouvel interprète Python sans causer d'effets secondaires non intentionnels (comme le démarrage d'un nouveau processus).

[Edit2] Y a-t-il une raison pour laquelle vous ne pouvez pas faire l'importation dans le processus? Je pense que le problème est que lorsque vous lancez le nouveau processus, il ne s'exécute pas dans le même espace d'adressage, donc essayer d'accéder aux fonctions dans le thread original ne fonctionnera pas.

Vous pourriez faire cela pour D:\Experiments\ModuleLoading\test.py à la place :

from multiprocessing import Process
import imp

class MyProcess(Process):
    def __init__(self):
        Process.__init__(self)

    def run(self):
        print 'appel de run...'
        module=imp.load_source('life', r'D:\Experiments\ModuleLoading\somefolder\life.py')
        print 'en cours d'exécution...', module.step()

if __name__=="__main__":
    p=MyProcess()
    p.start()

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