I'd like to be able to indicate to clients on connection that a
service is not ready due to something missing/wrong on the server
side. I thought I would do this by raising an exception in on_connect
but this didn't have the behaviour I expected, the exception is only
raised on the server side, the client side connect() call succeeds,
any subsequent attribute accesses however result in an EOFError. My
service is only really usable when it is in a specific state which is
dependent on some other third party software, when my service cannot
get into the correct state I'd like to be able to raise an appropriate
exception on the client side that will make it obvious to my users
that they need to check the server setup.
I wrote a simple script to illustrate what I'm trying to do:

import rpyc
from rpyc.utils.server import ThreadedServer

class MyService(rpyc.Service):
    """
        Example service that raises ValueError if server/service is not ready
    """
    def on_connect(self):
        """
            Initialize service - raises ValueError if service not ready
        """
        raise ValueError("Service not ready! (Please contact service admin)")

    @property
    def some_active_property(self):
        """
            This property will only return sensible data if the service is ready
        """
        raise ValueError("Service is not ready - cannot access active
properties")
    exposed_some_active_property = some_active_property

if __name__ == "__main__":
    t = ThreadedServer(MyService, port=18861)
    t.start()


Client side:

import rpyc
conn = rpyc.connect("127.0.0.1", 18861)
print conn.root.some_active_property


When running the client side code I get the following outputs:
Server side:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 505, in run
    self.__target(*self.__args, **self.__kwargs)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/utils/server.py",
line 165, in _authenticate_and_serve_client
    self._serve_client(sock2, credentials)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/utils/server.py",
line 190, in _serve_client
    conn._init_service()
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py",
line 146, in _init_service
    self._local_root.on_connect()
  File "server_side.py", line 13, in on_connect
    raise ValueError("Service not ready! (Please contact service admin)")
ValueError: Service not ready! (Please contact service admin)

Client side:

Traceback (most recent call last):
  File "client_side.py", line 4, in <module>
    print conn.root.some_active_property
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py",
line 459, in root
    self._remote_root = self.sync_request(consts.HANDLE_GETROOT)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py",
line 430, in sync_request
    self.serve(0.1)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py",
line 378, in serve
    data = self._recv(timeout, wait_for_lock = True)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/protocol.py",
line 336, in _recv
    data = self._channel.recv()
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/channel.py",
line 50, in recv
    header = self.stream.read(self.FRAME_HEADER.size)
  File "/usr/local/lib/python2.7/dist-packages/rpyc/core/stream.py",
line 175, in read
    raise EOFError("connection closed by peer")
EOFError: connection closed by peer

You can see the client's connect() call worked without exception, the
client is only made aware of the problem once it tries to access an
attribute of the service.
I can think of a few workarounds I could do - e.g. I could always
allow the client to connect and then add some extra logic in a client
side wrapper that polls some property on the server side to ensure the
service is ready for use. However I was thinking this kind of feature
might be useful to build into the service - in my case it's more
sensible for the service to decide whether or not it is in a useable
state - I'd also like to keep the client side code as thin as
possible.
Any help/suggestions would be much appreciated :)
Thanks,
Oliver

Reply via email to