2016-04-30 6 views
1

編集:これはディルドバグであることが判明しました。Python:Numpy.minは組み込み関数を置き換えます:Pyro4エラーの原因:応答シーケンスが同期外れ

編集:私はパイロ(バージョン4.43、Pythonの3.5で働いている分機能に建て

を交換numpy.minによって引き起こされることが判明し、エラーをトラブルシューティング私の進歩のために下へルック.1、Windows 10)、サーバープロセスがワーカーとワーカープロセスが作業を要求して結果を返すのを待つシンプルなクラスタを設定しようとしています。サーバーは結果を受け取ると、それ以上の処理を行います。

現在、私はただ1台のコンピュータで動作させようとしています(ローカルホストを使用し、同じコンピュータからワーカープロセスを起動させる)。

これまでのところ、サーバープロセスを実行することができました。ワーカープロセスはサーバーに接続してデータを要求し、そのデータを処理できましたが、結果を送信しようとするとワーカープロセスがエラーになりますサーバーに送信します。私は奇妙なエラーメッセージに実行しているよ

:徹底検索した後

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 

注:私は、これらのタイプを送信するために再帰的なオプションとディルを使用する必要がありますのデータ

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)の受信ループを持っています。何らかの形でこれが実行されると、組み込みminではなくnumpy.minが使用され、numpy.minの第2引数が軸番号であるため、エラーが出力されます。これは私のコードでnumpy as npをインポートしただけで、from numpy import *をインポートすることも、min関数を直接インポートすることも驚くべきことです。

さらに驚くべきことは、組み込み関数に置き換えることで修正できないということです。私はimport builtinsを試してmin = builtins.minを試しても、エラーは続く。 inspect.getfile(builtins.min)を実行すると、Numpyファイルを指します。

min([60000, size - msglen])の行を切り替えることで問題を完全に回避しようとしましたが、これはnumpyと組み込みのminの両方で機能しますが、min割り当てはサーバーコードに残っていて、そこの関数も混乱します。むしろハック修正として

、私は分関数の上記の変更を保持するだけでなく、私のサーバークラスの初期化時に、私は組み込み関数を保存:

#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'] 

これは現在動作しているようです。しかし、これは明らかに問題を解決する素晴らしい方法ではありません、私はむしろ最初の場所で組み込み関数を置き換えることをやめます...これはパイロ固有の問題ですか?

+0

Pyro自体は確かに組み込み部品を置き換えていません。別のライブラリがこれを行っている場合、すべてのベットはオフです。 それでは、内蔵分をnumpy.minに置き換える原因は何ですか?私がnumpyをインポートすると、何も起きていない。 builtins.minはPythonの独自の組み込み関数です。 –

+0

@Irmenご協力いただきありがとうございます。 Pyroが問題を引き起こしていないのはなぜだと思いますか?私はPyroなしでサーバーコードを完璧に動作させました。Pyroに接続すると問題が発生しました。私が 'numpy import min'を実行したとしても、それは' builtins.min'に取って代わるものではなく、minという名前だけを置き換えるという点で、非常に奇妙な問題です。 –

+0

私はPyroコードベース自体に組み込み関数を混乱させるものは何もないことを意味しました。さて、シリアライザーがやっていることは、私が知らないPyro(pickle、dill)によって呼び出されますが、おそらく彼らはそれを乱しています。これはPyroを使わないとコードがなぜ機能するのかを説明することができます。なぜなら、ピクル/ディルを使ってデータ構造を渡すとは思わないからです。 –

答えて

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

リターン:私はディルissue #167としてこれを提出した

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

次のコードは、エラーを再現します。

関連する問題