Fundamentals ============ sjRpc is a RPC (Remote Procedure Call) library written with Python. It was originally created for the needs of CloudControl project but can be reused in other projects. Features -------- * **Bidirectional:** remote function can be called by both side of the connection, so a client can connect to a server and export functions to it. * **Fully event based**: sjRpc takes profits of asynchronous io, allowing a server to handle thousands of client connection with a reasonable memory usage. * **Multiplexed:** sjRpc can run many "protocols" on the same connection, read more about protocols in `Multiplexing & protocols`_ section. * **Fallback mode:** for compatibility with olders sjRpc. Basic usage ----------- Server side, create the handler ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The handler is a simple object with a dict interface (getitem) responsible of the association between the remote-function name and the locally executed callable. The :class:`sjrpc.utils.Handler` class help you to define an handle with a simple class extending this one:: >>> class MyHandler(RpcHandler): ... def random(self, min=0, max=100): ... return random.randint(min, max) ... >>> handler = MyHandler() But if you want to use a standard dictionnary, this is exactly the same:: >>> handler = {'random': lambda min, max: random.randint(min, max)} Server side, create the :class:`~sjrpc.core.RpcServer` instance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The last thing to do on the server side is to launch the server itself:: >>> from sjrpc.server import RpcServer >>> serv = RpcServer.from_addr('127.0.0.1', 1337, conn_kw=dict(handler=handler)) >>> serv.run() .. note:: `conn_args` and `conn_kw` are the arguments which are automatically passed to each client :class:`~sjrpc.core.RpcConnection` instanciation. In this example, we just pass a default handler. Client side, just connect ! ^^^^^^^^^^^^^^^^^^^^^^^^^^^ For a basic client usage, the only thing you have to do is to create the :class:`~sjrpc.core.RpcConnection` instance to the server and start the :class:`~sjrpc.core.RpcConnection` main-loop in another thread to keep the hands on your term:: >>> conn = RpcConnection.from_addr('127.0.0.1', 1337) >>> threading.Thread(target=conn.run).start() >>> print conn.call('random') 42 You can also use a proxy to simplify remote calls:: >>> from sjrpc.utils import ConnectionProxy >>> proxy = ConnectionProxy(conn) >>> print proxy.random(min=42, max=1000) 587 Proxy will also restore built-in exceptions embedded in :class:`~sjrpc.core.RpcError`. Multiplexing & protocols ------------------------ Protocol of sjRpc use channels to multiplexe many protocols on the same connection. Each channel is binded to a protocol handler on each side, and each channel have a label which identify it on the wire. Actually, the sjRpc protocol look like this:: +------------+------------------------+---------------------------------+ | Label (2) | Payload size (4) | Payload (variable) | +------------+------------------------+---------------------------------+ For the moment, two types of protocols are implemented: * **Rpc:** protocol which allow to make remote function call easily. * **Tunnel:** protocol which allow to tunnel a socket through the sjRpc connection. To register new protocols, you can use the :meth:`register_protocol` on :class:`RpcConnection` instances, like this:: >>> from sjrpc.core.protocols import RpcProtocol, TunnelProtocol >>> my_tunnel = myconn.register_protocol(1, TunnelProtocol) do the same on other side, then >>> mytunnel.send('ehlo !!') >>> anwser = mytunnel.recv() You can also use shortcuts:: >>> my_tunnel = myconn.create_tunnel(label=12) or >>> my_tunnel = myconn.create_tunnel() # Bind to the first free label The same shortcut is available for rpc with :meth:`create_rpc`. Default rpc, aka Rpc0 --------------------- Rpc0 is an RpcProtocol binded by default with the "0" label. You can't unregister this protocol, and you can't disable this feature. Rpc0 is used internally by sjRpc to share special messages, and for compatibility with olders sjRpc (see `Fallback mode`_ below). However, you can use this rpc like any other, with your own rpc handler. Fallback mode ------------- The fallback mode makes the *sjrpc >= 14* compatible with olders version where channels and protocols doesn't exists. Old and new protocols are very similar, the new one just add a label field on each frame which allow multiplexing, but the rpc protocol itself has not changed, and is always using json messaging. The fallback mode is enabled by default when a connection is established (you can disable this behavior with the `disable_fallback` parameter of the :class:`RpcConnection` class constructor) and is automatically disabled when a special rpc message "capabilities" is received. .. note:: When the fallback mode is enabled, you can't use another protocols than the default rpc. All calls to :meth:`RpcConnection.register_protocol`, :meth:`RpcConnection.unregister_protocol`, :meth:`RpcConnection.create_rpc` and :meth:`RpcConnection.create_tunnel`, will fail with a :exc:`FallbackModeEnabledError`.