irmen / Pyro5

Pyro 5 - Python remote objects
https://pyro5.readthedocs.io
MIT License
305 stars 36 forks source link

How to request local copy of an object, having its Proxy? #43

Closed eudoxos closed 2 years ago

eudoxos commented 3 years ago

I am accessing a remote object via URI. In one case, I need to get a local copy of the remote object (the class is registered for serialization), as opposed to proxy. Currently, I have something like this:

@Pyro5.api.expose
class Foo(object):
    def localCopy(self): return self
foo=Foo()
daemon=Pyro5.api.Daemon()
uri=daemon.register(foo)
daemon.requestLoop()

remote:

proxy=Pyro5.api.Proxy(uri)
local=proxy.localCopy()
assert isinstance(local,Foo) # OK

As you see, I define localCopy() which effectively transfers the object. But I am not sure whether this is guaranteed, whether it can be forced (there is the autoproxy global config flag). And perhaps Proxy does have some method to get the object transferred?

irmen commented 3 years ago

The code you provided doesn't work. You're missing an @expose on the Foo class. After fixing that the client/remote code fails in the assert because the autoproxy mechanism returns a Proxy object... Have you disabled the autoproxying perhaps?

eudoxos commented 3 years ago

Thanks for the reaction, I added the missing @expose. I am not aware of disabling autoproxying. I was in fact looking in the config and the source to find how to enable/disable it and could not find anything - the autoproxy docs references Configuring Pyro, but there is nothing resembling autoproxying.

I will summarize my understanding, please correct me if it is wrong -- so that I have the right assumption before asking for more fine-grained control, if needed.

@expose
class Foo(): pass
@expose
class Bar():
    def getFoo(self):
        foo=Foo()
        if getattr(self,'_pyroDaemon'): self._pyroDaemon.register(foo) # conditional so that it works locally as well
        return foo
  1. when returning an exposed object (type Foo) from a method call getFoo on instance of another exposed object (fype Bar), then if the instance of Bar is registered (has _pyroDaemon) and the instance of Foo being returned is also registered in the same daemon, a Proxy is returned.
  2. otherwise a copy of foo (serialized and deserialized) will be returned .
irmen commented 3 years ago

I'm sorry I got confused myself a bit. The autoproxy mechanism was configurable in the distant past but is always enabled now.

As to your understanding: a Proxy is returned if the object returned is registered to any daemon, doesn't matter which. (This is actually a small bug because if the deamon is different to the daemon handling the request, it will return a proxy that's connected to a different daemon, and so will likely malfunction. I'm not going to look into that now.)

So- your example will always return a proxy. I don't understand how your first code could not have returned a proxy, when I run that (with the expose fix) it returns a proxy.

eudoxos commented 3 years ago

The code above is not a MWE, rather a hand-reduction of what I think it happening in this test:

        local=proxy.getLocalCopy()
        self.assertEqual(local.__class__,mp.heavydata.HeavyDataHandle)

with this definition of getLocalCopy:

   def getLocalCopy(self):
        return self

So it is doing what I want (local copy), but not what it should (auto-proxying). The same code uses auto-proxying now in other places, and it is really returning proxies (e.g. here. I will have to figure out what is happening.

If returning proxies is always active (for registered objects), is there a way to request local copy of the object on the other side of the proxy?

As to your understanding: a Proxy is returned if the object returned is registered to any daemon, doesn't matter which. (This is actually a small bug because if the deamon is different to the daemon handling the request, it will return a proxy that's connected to a different daemon, and so will likely malfunction. I'm not going to look into that now.)

I see this not as critical for our use ATM, although an assert which would signal a problem for the case of different daemons might be useful.

irmen commented 3 years ago

I don't understand why you would want a local instance of the object when talking to it remotely, but ok. You could try to wrap it inside another class that's not registered with a daemon, and return an instance of that. Then register a custom class deserializer in the client, that recreates the containing object instead. There's an example that shows how to do custom (de)serializers. I'm not 100% sure this is going to work but it's worth a try.

Also , the simplest answer; why not just create an object directly instead of asking for it through a remote call? in the client: localcopy = Foo() ....

eudoxos commented 3 years ago

Context explanation: the object is accessor to big data stored in HDF5. Sometimes it is convenient to call it remotely (if only few data are accessed — the remote access is much slower). For frequent access, it is faster to transfer the underlying HDF5 file first (this happens automatically, when deserializing the accessor), and then access using local accessor (with the same parameters).

What I did in the end: I (ab)used the serialization routines so I just request the remote object's serialized version (dictionary) through the proxy; this is transferred as-is. The local side thinks it should be deserialized (it contains __class__) and reconstructs the object locally. Done!

(I still don't know why the return self was returning a copy, not a Proxy.)

irmen commented 2 years ago

closing this as the original problem was worked around