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