0

I have an RpyC server that imports a specific module, a class inside this module should be exposed so that this class can then be inherited from the client side.

For testing purposes I removed the module importing/exposing and created a simple class inside my RPyC service called exposed_TestClass.

server side: rpyc_server.py

import rpyc
from rpyc.utils.server import ThreadedServer

class MyService(rpyc.Service):
    class exposed_TestClass:
        def exposed_Exec(self):
            print("original print of class")

t = ThreadedServer(MyService, port=12345)
t.start()

client side: python3 shell

>>> import rpyc
>>> conn = rpyc.connect("localhost", 12345)
>>> conn.root.TestClass
<class 'exposed_TestClass'>
>>> conn.root.TestClass()
<exposed_TestClass object at 0x7f2dda642588>
>>> #calling the Exec function also works, prints at server side
>>> conn.root.TestClass().Exec()
>>>
>>>
>>> # test inheriting class
>>> class MyClass(conn.root.TestClass):
...     def NewMethod(self):
...         print("printing from new method")
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3/dist-packages/rpyc/core/netref.py", line 220, in method
    return syncreq(_self, consts.HANDLE_CALLATTR, name, args, kwargs)
  File "/usr/lib/python3/dist-packages/rpyc/core/netref.py", line 74, in syncreq
    conn = object.__getattribute__(proxy, "____conn__")
AttributeError: 'str' object has no attribute '____conn__'

2 Answers2

0

As per the documentation on: https://rpyc.readthedocs.io/en/latest/tutorial/tut2.html

Voila, netrefs (network references, also known as transparent object proxies) are special objects that delegate everything done on them locally to the corresponding remote objects. Netrefs may not be real lists of functions or modules, but they “do their best” to look and feel like the objects they point to… in fact, they even fool python’s introspection mechanisms!

on the client side, if you do:

import rpyc
conn = rpyc.connect("localhost", 12345)

o=conn.root.TestClass()
type_o = type(o)
print(type_o)

it will print out:

<netref class 'rpyc.core.netref.__main__.exposed_TestClass'>

So when trying to do:

class MyClass(type_o):
    def NewMethod(self):
        print("printing from new method")

You inherit from the proxy class definition but not from the remote class definition. So when documentation states

they “do their best”

, i think it exclude the possibility of doing inheritance from a remote class. I'll be more than glad if ever someone can find a way to met your requirment.

Please note I made my tests by setting up the server using the following line:

t = ThreadedServer(MyService, port=12345, protocol_config={ 'allow_all_attrs': True })
  • You're right I haven't found any other way. The inheritance was needed from my side, because the library I am working with uses the inheritance for callbacks. In order to create a new callback, I had to create a new class that overrides the callback function of the base class. That's why I tried to expose the callback class with rpyc. My final solution was to expose a method to the client that takes a python function as argument, and on the server side, I would just wrap a new class around the given function everytime the method is called, which makes it even easier to attach a new callback. – ColrblindMartian Nov 22 '20 at 11:19
0

Server.py

import rpyc

from rpyc.utils.server import ThreadedServer


class ServerService(rpyc.Service):
    def exposed_bar(self, func):
        return func() + "Server said: Client get back this sentence"

print('starting server')
server = ThreadedServer(ServerService, port=18811)
server.start()

client.py

import rpyc
class ClientService(rpyc.Service):
    def exposed_foo(self):
        return "foo_exposed"

def foofunc():
    return "Client give this sentence\n"

conn = rpyc.connect("192.168.72.3", 18811, service=ClientService)
print("Connection Success {}\n".format(conn))
print(conn.root.bar(foofunc))
ben othman zied
  • 176
  • 1
  • 7