Python: How to pass an Autoproxy object

2019-04-16 05:48发布

I need to pass the proxy of an object to another object but whenever I do this all the other object gets is the referent of the proxy and not the proxy itself. This is something similar to what I am trying to do:

import multiprocessing.managers as m

class Foo(object):

    def __init__(self):
        self.manager = MyManager()
        self.manager.register('Bar', Bar)
        self.manager.start()
        self.bar = self.manager.Bar()
        self.bar.set_proxy(self.bar)


class Bar(object):

    def __init__(self):
        self.proxy = None

    def set_proxy(self, proxy):
        self.proxy = proxy

class MyManager(m.BaseManager):
    pass

test = Foo()

whenever I do this the value in self.proxy is the instance of Foo that I created and not the proxy that was returned by the manager.

1条回答
该账号已被封号
2楼-- · 2019-04-16 06:39

This is because of design flaw, or perhaps a bug, in the way Proxy instances get unpickled. Right now, if the unpickling code see you're running inside a Manager, it gives you the referent when you unpickle, rather than the Proxy:

def RebuildProxy(func, token, serializer, kwds):
    '''
    Function used for unpickling proxy objects.

    If possible the shared object is returned, or otherwise a proxy for it.
    '''
    server = getattr(process.current_process(), '_manager_server', None)

    if server and server.address == token.address:
        return server.id_to_obj[token.id][0] # This returns the referent
    else:
        incref = (
            kwds.pop('incref', True) and
            not getattr(process.current_process(), '_inheriting', False)
            )
        return func(token, serializer, incref=incref, **kwds) # This returns the Proxy

Sometimes this might be desirable, but sometimes its not, like it is in your case. You can work around it by replacing the ReduceProxy function that does the unpickling in your program:

import multiprocessing.managers as m
from multiprocessing import process

def RebuildProxyNoReferent(func, token, serializer, kwds):
    '''
    Function used for unpickling proxy objects.

    The Proxy object is always returned.
    '''
    incref = (
    kwds.pop('incref', True) and
    not getattr(process.current_process(), '_inheriting', False)
    )
    return func(token, serializer, incref=incref, **kwds)

m.RebuildProxy = RebuildProxyNoReferent  # Monkey-patch it

class Foo(object):

    def __init__(self):
        self.manager = MyManager()
        self.manager.register('Bar', Bar)
        self.manager.start()
        self.bar = self.manager.Bar()
        print(type(self.bar))
        self.bar.set_proxy(self.bar)


class Bar(object):

    def __init__(self):
        self.proxy = None

    def set_proxy(self, proxy):
        print("got {}".format(type(proxy)))
        self.proxy = proxy

class MyManager(m.BaseManager):
    pass

test = Foo()

If you don't want to monkey-patch, you can also subclass BaseProxy, though its a bit more work:

import multiprocessing.managers as m
from multiprocessing.managers import BaseProxy
from multiprocessing import process

def RebuildProxyNoReferent(func, token, serializer, kwds):
    '''
    Function used for unpickling proxy objects.

    If possible the shared object is returned, or otherwise a proxy for it.
    '''
    incref = (
    kwds.pop('incref', True) and
    not getattr(process.current_process(), '_inheriting', False)
    )
    return func(token, serializer, incref=incref, **kwds)

class MyProxy(BaseProxy):
    _exposed_ = ("set_proxy",)
    def set_proxy(self, arg):
        self._callmethod('set_proxy', (arg,))

    def __reduce__(self):
        ret = super(MyProxy, self).__reduce__()
        # RebuildProxy is the first item in the ret tuple.
        # So lets replace it, just for our proxy.
        ret = (RebuildProxyNoReferent,) + ret[1:]
        return ret

class Foo(object):

    def __init__(self):
        self.manager = MyManager()
        self.manager.register('Bar', Bar, MyProxy)
        self.manager.start()
        self.bar = self.manager.Bar()
        print(type(self.bar))
        self.bar.set_proxy(self.bar)
查看更多
登录 后发表回答