2011-11-14 7 views
5

Encontré un problema que no puedo resolver y está asociado con el multiprocesamiento y lo uso dentro del decorador.Usar el multiprocesamiento dentro del decorador genera un error: no se puede recuperar la función ... no se encuentra como

Cuando estoy llamando al método run_in_parallels usando multiprocesamiento que estoy consiguiendo el error:

Can't pickle <function run_testcase at 0x00000000027789C8>: it's not found as __main__.run_testcase

La llamada se lleva a cabo en el interior del decorador, a continuación, siguieron el problema antes mencionado. En el momento de llamar al mismo método run_in_parallels sin un decorador, todos funcionan correctamente.

¿Cuál es la razón de este problema?


archivo: w_PythonHelper.py

desc: Función 'run_in_parallel' se utiliza para ejecutar varios procesos al mismo tiempo. El primer método, que finalizará la operación, detiene a los demás.

from multiprocessing import Process,Event 

class ExtProcess(Process): 
    def __init__(self, event,*args,**kwargs): 
     self.event=event 
     Process.__init__(self,*args,**kwargs) 

    def run(self): 
     Process.run(self) 
     self.event.set() 

class PythonHelper(object): 
    @staticmethod 
    def run_in_parallel(*functions): 
     event=Event() 
     processes=dict() 
     for function in functions: 
      fname=function[0] 
      try:fargs=function[1] 
      except:fargs=list() 
      try:fproc=function[2] 
      except:fproc=1 
      for i in range(fproc): 
       process=ExtProcess(event,target=fname,args=fargs) 
       process.start() 
       processes[process.pid]=process 
     event.wait() 
     for process in processes.values(): 
      process.terminate() 
     for process in processes.values(): 
      process.join() 

archivo: w_Recorder.py

desc: función de 'captura' se utiliza para tomar una captura de pantalla

from PIL import ImageGrab 
import time 

class Recorder(object): 
    def capture(self): 
     ImageGrab.grab().save("{f}.{e}".format(f=time.time(),e="png")) 

archivo: w_Decorators.py

desc: que corren paralelas una función dada junto con un método de la 'captura' de clase 'Grabadora'

from w_Recorder import Recorder 
from w_PythonHelper import PythonHelper 

def check(function): 
    def wrapper(*args): 
     try: 
      recorder=Recorder() 
      PythonHelper.run_in_parallel([function,args],[recorder.capture]) 
      print("success") 
     except Exception as e: 
      print("failure: {}".format(e)) 
     return function 
    return wrapper 

archivo: w_Logger.py

desc: programa principal (genera error)

from w_Decorators import check 
import time 

class Logger(object): 

    @check 
    def run_testcase(self): 
     # example function (runtime: 20s) 
     for i in range(20): 
      print("number: {}".format(i)) 
      time.sleep(1) 

    def run_logger(self): 
     self.run_testcase() 


if __name__=="__main__": 
    logger=Logger() 
    logger.run_logger() 

archivo: w_Logger.py

desc: programa principal (trabaja corectly)

from w_PythonHelper import PythonHelper 
from w_Recorder import Recorder 
import time 

class Logger(object): 

    def run_testcase(self): 
     # example function (runtime: 20s) 
     for i in range(20): 
      print("number: {}".format(i)) 
      time.sleep(1) 

    def run_logger(self): 
     recorder=Recorder() 
     PythonHelper.run_in_parallel([self.run_testcase],[recorder.capture]) 

if __name__=="__main__": 
    logger=Logger() 
    logger.run_logger() 

¿Cuál es la diferencia de que estos mismos métodos que se presentan en los dos casos funcionan de forma diferente?


EDIT: ¿Alguien tiene una idea para resolver este problema (es este error de Python)? Si no, ¿alguien sabe una buena manera de capturar capturas de pantalla cuando la aplicación se está ejecutando?

En realidad me pareció pregunta similar: multiprocessing.Process subclass works on Linux but not Windows

La respuesta es: To fix this, you can remove the process member., pero ¿cómo se puede hacer esto por mi ejemplo.

Al depurar el error se produce cuando se llama a process.start() en run_in_parallel(*functions)


Edit2: como ivan_pozdeev escribió: puedo utilizar envoltorio como una función, pero no se puede utilizar como decorador. Tengo muchas funciones decoradas por este decorador y la manera más simple es usar multiprocesamiento dentro del decorador. Pero desafortunadamente no puedo resolver este problema. Quizás alguien ya haya resuelto un problema similar. Estaría agradecido por cualquier pista.

'run_in_parallel' Funciona como yo quiero. Dos o más funciones se ejecutan en paralelo y la primera función, que se completa, fuerza la terminación de la segunda función. Cuando llamo al wrapper (función, * args) entonces las funciones funcionan bien, cuando pongo este mecanismo dentro del decorador obtengo 'no puedo saltear la función ... no se encuentra como' error. Los detalles pueden encontrarse por encima de

Mi Rastreo:

Traceback (most recent call last): 
    File "C:\Interpreters\Python32\lib\pickle.py", line 679, in save_global 
    klass = getattr(mod, name) 
AttributeError: 'module' object has no attribute 'run_testcase' 

During handling of the above exception, another exception occurred: 

Traceback (most recent call last): 
    File "C:\EskyTests\w_Logger.py", line 19, in <module> 
    logger.run_logger() 
    File "C:\EskyTests\w_Logger.py", line 14, in run_logger 
    self.run_testcase() 
    File "C:\EskyTests\w_Decorators.py", line 14, in wrapper 
    PythonHelper.run_in_parallel([function,args],[recorder.capture]) 
    File "C:\EskyTests\w_PythonHelper.py", line 25, in run_in_parallel 
    process.start() 
    File "C:\Interpreters\Python32\lib\multiprocessing\process.py", line 130, in start 
    self._popen = Popen(self) 
    File "C:\Interpreters\Python32\lib\multiprocessing\forking.py", line 267, in __init__ 
    dump(process_obj, to_child, HIGHEST_PROTOCOL) 
    File "C:\Interpreters\Python32\lib\multiprocessing\forking.py", line 190, in dump 
    ForkingPickler(file, protocol).dump(obj) 
    File "C:\Interpreters\Python32\lib\pickle.py", line 237, in dump 
    self.save(obj) 
    File "C:\Interpreters\Python32\lib\pickle.py", line 344, in save 
    self.save_reduce(obj=obj, *rv) 
    File "C:\Interpreters\Python32\lib\pickle.py", line 432, in save_reduce 
    save(state) 
    File "C:\Interpreters\Python32\lib\pickle.py", line 299, in save 
    f(self, obj) # Call unbound method with explicit self 
    File "C:\Interpreters\Python32\lib\pickle.py", line 623, in save_dict 
    self._batch_setitems(obj.items()) 
    File "C:\Interpreters\Python32\lib\pickle.py", line 656, in _batch_setitems 
    save(v) 
    File "C:\Interpreters\Python32\lib\pickle.py", line 299, in save 
    f(self, obj) # Call unbound method with explicit self 
    File "C:\Interpreters\Python32\lib\pickle.py", line 683, in save_global 
    (obj, module, name)) 
_pickle.PicklingError: Can't pickle <function run_testcase at 0x00000000027725C8>: it's not found as __main__.run_testcase 

Respuesta

1

complicado, pero yo creo lo que está sucediendo es que check almacena una referencia al método no unido cuando se define la clase. El ejemplo de trabajo utiliza una referencia al método enlazado self.run_testcase cuando llama al run_logger.

Creo que lo mejor es intentar hacer de run_testcase una función de nivel superior, en lugar de un método de una clase.

Además, su función capture probablemente no hará lo que espera: la hora actual se almacenará cuando se defina la función, y cada captura de pantalla se guardará con respecto a la anterior. Probablemente desee llamar al time.time() dentro de la función.

+0

Cuando estoy haciendo 'run_testcase' una función de nivel superior como mencionaste, recibo otro error:' No puedo saltear : no es el mismo objeto que __main __. Run_testcase_proc'. Desafortunadamente debe ser un método de una clase. Tengo que modificar el decorador existente para adoptar la nueva funcionalidad que se ejecutará en paralelo con 'run_testcase'. La función 'time.time()' es un problema menor y solo se usa para mantener un orden, podría ser fácilmente un número, por ejemplo 1,2,3 ... El problema principal era modificar el decorador. –

+1

@ falek.marcin: de los documentos de multiprocesamiento: "los métodos enlazados o no unidos no se pueden usar directamente como el argumento de destino en Windows" (http://docs.python.org/dev/library/multiprocessing#windows) –

+0

Puede haber una solución alternativa o alternativa para modificar el decorador. ¿Alguien tiene una idea de cómo resolverlo de manera diferente? –

4

La función que está pasando al Process.__init__() no es seleccionable en Windows. Lee 16.6 multiprocessing - Programming guidelines - Windows.

En cuanto a su error con una función de nivel superior, sospecho que la forma en que lo definió, se genera de manera diferente cada vez y por lo tanto, realmente no es "el mismo objeto" en el niño. Sugiero pasar una función simple de nivel superior que llame al run_testcase usando reflexión si realmente necesita este nivel de sofisticación. Actualización: esto no ayuda

Actualización:

Hice este trabajo por undecorating run_testcase, run_in_parallel y capture.@check decorador fue sustituido por def wrapper(function,*args) con la misma funcionalidad:

import traceback 
def wrapper(function,*args): 
    try: 
     recorder=Recorder() 
     PythonHelper().run_in_parallel([function,args],[recorder.capture]) 
     print("success") 
    except Exception,e: 
     print("failure: "+traceback.format_exc(10)) 

principal:

from w_Decorators import wrapper 

if __name__=="__main__": 
    logger=Logger() 
    wrapper(logger.run_testcase) 

Justo lo que pensaba - objetos decorados no son estibables.

+0

¿Podría mostrar cómo lo haría basado en mi ejemplo? –

+0

Ver mis ediciones arriba. Por cierto, la lógica en run_in_parallel() puede ser incorrecta. –

+0

_ @ check decorator fue reemplazado por def wrapper (function, * args) con la misma funcionalidad_ no, esta no es la misma funcionalidad. Tenga en cuenta que escribí que ** Tengo que usar ** una sintaxis de decorador. Solo tengo muchos métodos que están decorados de esa manera. –

Cuestiones relacionadas