Erle Robotics Python Networking Gitbook Free

An RPyC Example

The RPyC project lives here: http://rpyc.wikidot.com/

This project takes a much more sophisticated approach toward objects. Indeed, where what actually gets passed across the network is a reference to an object that can be used to call back and invoke more of its methods later if the receiver needs to. The most recent version also seems to have put more thought into security, which is important if you are letting other organizations use your RPC mechanism. After all, if you let someone give you some data to un-pickle, you are essentially letting them run arbitrary code on your computer.

You can see an example client and server in Listings rpyc_client.pyand rpyc_server.py. If you want an example of the incredible kinds of things that a system like RPyC makes possible, you should study these listings closely.


import rpyc

def noisy(string):
    print 'Noisy:', repr(string)

proxy = rpyc.connect('localhost', 18861, config={'allow_public_attrs': True})
fileobj = open('testfile.txt')
linecount = proxy.root.line_counter(fileobj, noisy)
print 'The number of lines in the file was', linecount

At first the client might look like a rather standard program using an RPC service. After all, it calls a generically-named connect() function with a network address, and then accesses methods of the returned proxy object as though the calls were being performed locally.

The server exposes a single method that takes the proffered file object and callable function. It uses these exactly as you would in a normal Python program that was happening inside a single process. It calls the file object’s readlines() and expects the return value to be an iterator over which a for loop can repeat. Finally, the server calls the function object that has been passed in without any regard for where the function actually lives (namely, in the client).



import rpyc

class MyService(rpyc.Service):
    def exposed_line_counter(self, fileobj, function):
        for linenum, line in enumerate(fileobj.readlines()):
            function(line)
        return linenum + 1

from rpyc.utils.server import ThreadedServer
t = ThreadedServer(MyService, port = 18861)
t.start()

It is especially instructive to look at the output generated by running the client, assuming that a small testfile.txt indeed exists in the current directory and that it has a few words of wisdom inside:

root@erlerobot:~/Python_files# python rpyc_client.py
Noisy: 'Simple\n'
Noisy: 'is\n'
Noisy: 'better\n'
Noisy: 'than\n'
Noisy: 'complex.\n'
The number of lines in the file was 5

Equally startling here are two facts. First, the server was able to iterate over multiple results from readlines(), even though this required the repeated invocation of file-object logic that lived on the client. Second, the server didn’t somehow copy the noisy() function’s code object so it could run the function directly; instead, it repeatedly invoked the function, with the correct argument each time, on the client side of the connection.

RPyC takes exactly the opposite approach from the other RPC mechanisms we have looked at. Whereas all of the other techniques try to serialize and send as much information across the network as possible, and then leave the remote code to either succeed or fail with no further information from the client, the RPyC scheme only serializes completely immutable items such as Python integers, floats, strings, and tuples. For everything else, it passes across an object name that lets the remote side reach back into the client to access attributes and invoke methods on those live objects.