如何将函数发送给远程Pyro对象 [英] How to send a function to a remote Pyro object

查看:134
本文介绍了如何将函数发送给远程Pyro对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Pyro设置一些代码来在远程主机上处理python代码函数并返回结果。在启动名称服务器后,我会在远程主机上执行这段代码(实际上仍然在本地主机上):

  import Pyro4 

类服务器(对象):
def评估(self,func,args):
返回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:Us erWarning:HMAC_KEY未设置,
协议数据可能不安全
warnings.warn(HMAC_KEY未设置,协议数据可能不安全)
Traceback(最近一次调用最后一次):
文件/home/davide/Projects/rempy/example-api-pyro.py,第7行,位于< module>
print remoteServer.evaluate(square,4)
文件/usr/lib/python2.7/site-packages/Pyro4/core.py,第149行,在__call__
中返回self .__ send(self .__ name,args,kwargs)
文件/usr/lib/python2.7/site-packages/Pyro4/core.py,第289行,在_pyroInvoke
提升数据
AttributeError:'module'对象没有属性'square'

在我看来,函数对象被正确地腌制并被发送到远程主机上的服务器实例,但命名空间中存在一些问题。



我该如何解决这个问题?



谢谢

解决方案

我想我知道你的问题: b
$ b

该函数被定义的模块称为

 '__ main__'

它存在于所有正在运行的python版本中。



pickle不会传输源代码,但会引用

  __ main __。square 

所以你有两种可能:

源广场,并使主模块尽可能短,如:

 #main。 py 
$ b def square(x):
return x ** 2

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


print remoteServer.evaluate(square,4)



 #__main__.py 
导入主要
主要.main()

然后服务器可以从文件中导入完全相同的模块。



或者用我的代码创建一个模块:

  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 ThisShallN everBeCalledError()

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


def dump_and_load(obj):
'''pickle并取消对象一次'''
s = pickle.dumps(obj)
return pickle.loads(s)

#这个字符串创建一个匿名类型的对象,可以调用
#来创建一个R对象,或者可以通过pickle减少
#并且在不加密的时候创建另一个匿名类型
#由于它不是一个类,所以您可能不会继承此MetaR对象
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()'):
'''返回执行的对象unbickled时全局变量代码
use_same_globals $ b $如果use_same_globals为True所有通过
发送的代码一个pickle连接共享相同的全局变量
默认不变
return_value_variable_name
如果一个名为return_value_variable_name的变量在代码执行后在globals中存在

则作为酸洗操作返回
如果不是则返回无
__name__

'''
如果check_syntax:
编译(代码,'','exec')
#复制当地人很重要
#当地人通过pickle对于所有代码相同的
#复制它可以防止在同一个全局变量中执行不同的代码be
如果不是use_same_globals:
globals = globals.copy()
if add_builtins:
globals ['__ builtins__'] = PICKLABLE___builtins__
globals.setdefault('obj',None)
#获取编译代码
#不编码或反编码代码对象,因为平台可能会变化
code = R(compile,code,__name__,'exec')
#可以减少,转储和加载的最终对象
obj = R(R(getattr,tuple,'__getitem__') ,(
R(R(PICKLABLE_FunctionType,code,globals)),
R(R(getattr,type(globals),'get'),全局变量,\
returnValueVariableName,None)
),-1)
return obj

然后发送给其他方面:

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

并且函数会出现在另一边,否我们需要使用模块代码来将这个python函数传递给另一个服务器端。



如果有什么不行的话请告诉我。


I am trying to set up some code using Pyro to process python code functions on a remote host and get results back. After starting the name server, i would execute this code on the remote host (actually still on localhost):

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

On the client side i have this code, which is an example of the behaviour i am trying to set up.

import Pyro4

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

def square(x): 
    return x**2

print remoteServer.evaluate(square, 4)

However, this code results in the following exception:

/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'

It seems to me that the function object is pickled correctly and is sent to the Server instance on the remote host, but there is some problem in the namespace.

How can i solve this problem?

Thanks

解决方案

I think i know your problem:

the module the function is defiined in is called

'__main__'

it exists in all running versions of python.

pickle does not transfer the source code but a reference

__main__.square

so you have two possibilities:

source square out and make the main module as short as possible such as:

# main.py

def square(x): 
   return x**2

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


    print remoteServer.evaluate(square, 4)

and:

# __main__.py
import main
main.main()

Then the server can import exactly the same module from the file.

or create a module with my code:

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

and then send this to the other side:

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

and the function will come out on the other side, no module code is needed to transefer this python function to the other server side.

If something does not work out please tell me.

这篇关于如何将函数发送给远程Pyro对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆