2016-04-30 30 views
1

編輯:原來是一個蒔蘿bug,請參閱答案。Python:Numpy.min替換內建:導致Pyro4錯誤:回覆序列不同步

編輯:放眼底部我的進步錯誤後,這原來是由numpy.min更換而造成的在建min函數

我與煙火(版本4.43,Python的3.5工作.1,Windows 10),並嘗試設置一個簡單的羣集,其中服務器進程等待工作進程和工作進程請求工作併發回結果。一旦服務器收到結果,它會對它進行一些進一步的處理。

目前我只是試圖讓它在單臺計算機上工作(使用本地主機和產卵工作進程從同一臺計算機)。

到目前爲止,我能夠讓服務器進程運行,並且工作進程能夠連接到服務器以請求數據,處理該數據,但是當工作進程嘗試將結果發回時工作進程出錯到服務器。

我遇到一個奇怪的錯誤消息:

File "worker.py", line 90, in <module> 
    main() 
    File "worker.py", line 87, in main 
    worker.send_result() 
    File "worker.py", line 49, in send_result 
    self.server.recieve(result) 
    File "C:\Anaconda3\lib\site-packages\Pyro4\core.py", line 171, in __call__ 
    return self.__send(self.__name, args, kwargs) 
    File "C:\Anaconda3\lib\site-packages\Pyro4\core.py", line 418, in _pyroInvoke 
    self.__pyroCheckSequence(msg.seq) 
    File "C:\Anaconda3\lib\site-packages\Pyro4\core.py", line 448, in __pyroCheckSequence 
    raise errors.ProtocolError(err) 
Pyro4.errors.ProtocolError: invoke: reply sequence out of sync, got 0 expected 2 

徹底搜索後,我只能找到一個other person who has had this error,但反應是,這是一個純粹的火焰兵的錯誤,他需要更新火焰兵,但是當我寫這篇文章時,我的版本遠遠超出了現在的版本。

更進一步,我在生成代碼之外重現此錯誤時遇到了問題。我試圖創建一個簡單的版本來縮小錯誤的來源,並且無法獲得此錯誤。我甚至從工作人員那裏發送了一個結果的確切形式的結果,該結果以生產代碼發送,沒有錯誤。

這裏是簡化的代碼,只是爲了說明我的設置的結構。 下面的代碼不會重現錯誤。我不知道下一步是如何讓它更接近生產代碼而不會過度複雜化。

Server代碼:

#simple_server.py 

import Pyro4 
import sys, dill 

class SimpleServer: 

    def serve(self): 
     with open('served data.pkl', 'rb') as f: 
      data = dill.load(f) #actual data coming from production code 
     return data 

    def recieve(self, result): 
     print(result) 

def main(): 
    Pyro4.config.SERIALIZER = 'dill' #default serpent serializer doesn't work 
    dill.settings['recurse'] = True #dill won't work without this option 

    server = SimpleServer() 
    daemon = Pyro4.Daemon() 
    server_uri = daemon.register(server) 
    ns = Pyro4.locateNS() 
    ns.register("test", server_uri) 
    print('Server running.') 
    daemon.requestLoop() 

if __name__ == '__main__': 
    main()  

工人代碼:

#simple_worker.py 

import Pyro4 
import sys, dill 
import numpy as np 
import scipy.optimize as opt 

class SimpleWorker: 

    def __init__(self, server): 
     self.server = server 

    def recieve_data(self): 
     self.data = self.server.serve() 

    def send_result(self): 
     res = opt.basinhopping(lambda x: sum(x), np.arange(11), niter=2, minimizer_kwargs={'options':{'maxiter':2}}) 
     #This below data structure is the same that I send in production 
     result = ('ABCD', 'filename.csv', res, 6) 
     self.server.recieve(result) #creates error in production code but not here 

def main(): 
    sys.excepthook = Pyro4.util.excepthook #gives a more meaningful stack trace 
    Pyro4.config.SERIALIZER = 'dill' #default serpent serializer doesn't work 
    dill.settings['recurse'] = True #dill won't work without this option 

    server = Pyro4.Proxy('PYRONAME:test') #connects to pinest server 
    worker = SimpleWorker(server) 
    worker.recieve_data() 
    worker.send_result() 

if __name__ == '__main__': 
    main() 

的Windows CMD代碼:

#run_simple_server.bat 
set PYRO_SERIALIZERS_ACCEPTED=serpent,json,marshal,pickle,dill 
start cmd /C python -m Pyro4.naming 
python simple_server.py 
pause 

#run_simple_worker.bat 
python simple_worker.py 
pause 

注:我需要用蒔蘿使用遞歸選項發送這些類型的數據

如果我在worker main中打印Pyro4.current_context.seq,它將返回0.如果我嘗試Pyro4.current_context.seq = 2,它不會影響錯誤。

有誰知道如何處理這個錯誤或我應該在嘗試排除故障時做什麼?

編輯:經過對Pyro4源代碼的回顧後,似乎由於Pyro4中的編碼錯誤而引發此錯誤。在core.Daemon.handleRequest中,如果接收到消息時出錯,它將自己的消息序列設置爲零,並嘗試將該錯誤作爲消息發送。但是當core.Proxy。_pyroInvoke接收到消息,如果序列爲零,則無法將其視爲錯誤。因此,提出了應答序列不同步錯誤。

我已經想出了導致接收消息錯誤的潛在問題。 socketutil.receiveData具有一個接收循環,其中一行選擇最小值60000和剩餘大小的消息min(60000, size - msglen)。不知何故,當它執行時,它使用numpy.min而不是內建min,並且出錯,因爲numpy.min的第二個參數應該是軸號。這是令人驚訝的,因爲我只在我的代碼中導入了numpy as np,並且從未使用過from numpy import *或直接導入min函數。

更令人驚訝的是,我無法通過用內置函數替換它來修復它。我嘗試import builtins,然後min = builtins.min,錯誤仍然存​​在。如果我運行inspect.getfile(builtins.min)它指向Numpy文件。

我試圖通過切換線爲min([60000, size - msglen]),它適用於numpy和建立在最小,但最小分配持久回到我的服務器代碼並在那裏也弄亂功能,以儘量避免該問題。

作爲一個相當hackish的修復,我不停的分功能的上述變化,而且在我的服務器類的初始化,我存儲內置函數:

#Store builtin functions as they later get replaced for some unknown reason 
b = [t for t in().__class__.__base__.__subclasses__() if t.__name__ == 'Sized'][0].__len__.__globals__['__builtins__'] 
self.real_builtins = copy.copy(b) #copy so that dict doesn't get updated 

然後每次服務器接收或發送數據,我先運行這個函數:

def fix_builtins(self): 
    global builtins 
    import builtins 
    __builtins__ = self.real_builtins 
    #These are all of [i for i in dir(builtins) if i in dir(numpy)] 
    builtins.abs = __builtins__['abs'] 
    builtins.all = __builtins__['all'] 
    builtins.any = __builtins__['any'] 
    builtins.bool = __builtins__['bool'] 
    builtins.complex = __builtins__['complex'] 
    builtins.float = __builtins__['float'] 
    builtins.int = __builtins__['int'] 
    builtins.max = __builtins__['max'] 
    builtins.min = __builtins__['min'] 
    builtins.object = __builtins__['object'] 
    builtins.round = __builtins__['round'] 
    builtins.str = __builtins__['str'] 
    builtins.sum = __builtins__['sum'] 

這似乎現在工作。但是,這顯然不是一個很好的解決問題的方法,我寧願停止它從一開始就取代內建函數......這是Pyro特有的問題嗎?

+0

Pyro本身絕對不會取代builtins。如果另一個圖書館正在這樣做,所有投注都關閉。 所以:是什麼導致內置min被numpy.min替換?如果我輸入numpy,沒有什麼奇怪的事情發生。 builtins.min仍然是Python自己的內置。 –

+0

@Imen感謝您的幫助。你爲什麼如此確定Pyro不會導致問題呢?我的服務器代碼在沒有Pyro的情況下工作得很好,一旦我把它連接到Pyro,我就遇到了這個問題。這是一個非常奇怪的問題,因爲即使我從'numpy import min',它並不會取代'builtins.min',只有名字min。 –

+0

我的意思是說Pyro代碼庫本身沒有任何東西與內置函數搞混了。現在,*序列化程序*可能會做什麼,被Pyro(pickle,dill)調用,我不知道,但也許他們正在搞亂它。這仍然可以解釋爲什麼你的代碼不使用Pyro,因爲我不認爲你是通過pickle/dill傳遞數據結構。 –

回答

1

這是一個蒔蘿臭蟲,它是由酸洗Sympy表達式引起的。下面的代碼重新錯誤:

from sympy import symbols, lambdify 
import dill, inspect 

def check_if_builtin(func): 
    try: 
     file = inspect.getsourcefile(func) #will throw TypeError for builtin 
     return file 
    except TypeError: 
     return True 



dill.settings['recurse'] = True #without this option, throws PicklingError 

a, b, c = symbols("a b c") 
expr = a + b + c 
lambda_expr = lambdify([a, b, c], expr) 

print(check_if_builtin(min)) 

dill.dump(lambda_expr, open('test.p', 'wb')) 

print(check_if_builtin(min)) 

回報:

True 
C:\Anaconda3\lib\site-packages\numpy\core\fromnumeric.py 

我已經提交了該爲蘿issue #167

相關問題