2012-05-12 4 views
2

Pyroを使用してリモートホスト上のPythonコード関数を処理し、結果を返すためにいくつかのコードを設定しようとしています。ネームサーバを起動した後、私は(実際にはまだローカルホスト上の)リモートホスト上でこのコードを実行します:クライアント側でリモートPyroオブジェクトに関数を送信する方法

import Pyro4 

class Server(object): 
    def evaluate(self, func, args): 
     return func(*args) 

def main(): 
    server = Server() 
    Pyro4.Daemon.serveSimple(
      { 
       server: "server" 
      }, 
      ns=True) 

if __name__ == '__main__': 
    main() 

私が設定しようとしています行動の一例であるこのコードを、持っていますアップ。

import Pyro4 

remoteServer = Pyro4.Proxy('PYRONAME:server') 

def square(x): 
    return x**2 

print remoteServer.evaluate(square, 4) 

ただし、以下の例外で、このコードの結果:

/usr/lib/python2.7/site-packages/Pyro4/core.py:155: UserWarning: HMAC_KEY not set, 
protocol data may not be secure 
warnings.warn("HMAC_KEY not set, protocol data may not be secure") 
Traceback (most recent call last): 
    File "/home/davide/Projects/rempy/example-api-pyro.py", line 7, in <module> 
    print remoteServer.evaluate(square, 4) 
    File "/usr/lib/python2.7/site-packages/Pyro4/core.py", line 149, in __call__ 
    return self.__send(self.__name, args, kwargs) 
    File "/usr/lib/python2.7/site-packages/Pyro4/core.py", line 289, in _pyroInvoke 
    raise data 
AttributeError: 'module' object has no attribute 'square' 

関数オブジェクトが正しく漬けされ、リモートホスト上のサーバーインスタンスに送信されますが、そこにあると私には思えます名前空間におけるいくつかの問題。

どのように私はこの問題を解決することができますか?私は私はあなたの問題を知っていると思う

おかげ

答えて

2

モジュール関数がでdefiinedさは、それは、Pythonの実行中のすべてのバージョンに存在する

'__main__' 

と呼ばれています。

ソース広場アウトや、できるだけメインモジュールは限り短くします:

# main.py 

def square(x): 
    return x**2 

import Pyro4 
def main(): 
    remoteServer = Pyro4.Proxy('PYRONAME:server') 


    print remoteServer.evaluate(square, 4) 

漬物はそうあなたは二つの可能性を持っているソースコードが、参照

__main__.square 

を転送しません。

と:

その後、サーバーはファイルからまったく同じモジュールをインポートできます。

または私のコードでモジュールを作成します。その後、

class ThisShallNeverBeCalledError(Exception): 
    pass 

class _R(object): 
    def __init__(self, f, *args): 
     self.ret = (f, args) 
    def __reduce__(self): 
     return self.ret 
    def __call__(self, *args): 
     raise ThisShallNeverBeCalledError() 

    @classmethod 
    def fromReduce(cls, value): 
     ret = cls(None) 
     ret.ret = value 
     return ret 


def dump_and_load(obj): 
    '''pickle and unpickle the object once''' 
    s = pickle.dumps(obj) 
    return pickle.loads(s) 

# this string creates an object of an anonymous type that can 
# be called to create an R object or that can be reduced by pickle 
# and creates another anonymous type when unpickled 
# you may not inherit from this MetaR object because it is not a class 
PICKLABLE_R_STRING= "type('MetaR', (object,), " \ 
        "  {'__call__' : lambda self, f, *args: "\ 
        "   type('PICKLABLE_R', "\ 
        "    (object,), "\ 
        "    {'__reduce__' : lambda self: (f, args), "\ 
        "    '__module__' : 'pickleHelp_', "\ 
        "    '__name__' : 'PICKLABLE_R', "\ 
        "    '__call__' : lambda self: None})(), "\ 
        "  '__reduce__' : lambda self: "\ 
        "   self(eval, meta_string, "\ 
        "    {'meta_string' : meta_string}).__reduce__(), "\ 
        "  '__module__' : 'pickleHelp_', "\ 
        "  '__name__' : 'R'})()".replace(' ', '') 
PICKLABLE_R = _R(eval, PICKLABLE_R_STRING, \ 
       {'meta_string' : PICKLABLE_R_STRING}) 
R = dump_and_load(PICKLABLE_R) 
del PICKLABLE_R, PICKLABLE_R_STRING 

PICKLABLE___builtins__ = R(vars, R(__import__, '__builtin__')) 
PICKLABLE_FunctionType = R(type, R(eval, 'lambda:None')) 

##R.__module__ = __name__ 
##R.__name__ = 'PICKLABLE_R' 


def packCode(code, globals = {}, add_builtins = True, use_same_globals = False, \ 
      check_syntax = True, return_value_variable_name = 'obj', 
      __name__ = __name__ + '.packCode()'): 
    '''return an object that executes code in globals when unpickled 
use_same_globals 
    if use_same_globals is True all codes sent through 
    one pickle connection share the same globals 
    by default the dont 
return_value_variable_name 
    if a variable with the name in return_value_variable_name exists 
    in globals after the code execution 
    it is returned as result of the pickling operation 
    if not None is returned 
__name__ 

''' 
    if check_syntax: 
     compile(code, '', 'exec') 
    # copying locals is important 
    # locals is transferred through pickle for all code identical 
    # copying it prevents different code from beeing executed in same globals 
    if not use_same_globals: 
     globals = globals.copy() 
    if add_builtins: 
     globals['__builtins__'] = PICKLABLE___builtins__ 
    globals.setdefault('obj', None) 
    # get the compilation code 
    # do not marshal or unmarshal code objects because the platforms may vary 
    code = R(compile, code, __name__, 'exec') 
    # the final object that can reduce, dump and load itself 
    obj = R(R(getattr, tuple, '__getitem__'), (
      R(R(PICKLABLE_FunctionType, code, globals)), 
      R(R(getattr, type(globals), 'get'), globals, \ 
       returnValueVariableName, None) 
      ), -1) 
    return obj 

と反対側にこれを送る:

packCode(''' 
def square(...): 
    ... 
''', return_value_variable_name = 'square') 

と機能は反対側に出てくるだろう、何のモジュールのコードではありませんこのPython関数を他のサーバー側に移す必要があります。

何か問題が解決しない場合は教えてください。

関連する問題