09.11.2016 Views

Foundations of Python Network Programming 978-1-4302-3004-5

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

CHAPTER 18 ■ RPC<br />

linecount = proxy.root.line_counter(fileobj, noisy)<br />

print 'The number <strong>of</strong> lines in the file was', linecount<br />

At first the client might look like a rather standard program using an RPC service. After all, it calls a<br />

generically-named connect() function with a network address, and then accesses methods <strong>of</strong> the<br />

returned proxy object as though the calls were being performed locally. However, if you look closer, you<br />

will see some startling differences! The first argument to the RPC function is actually a live file object that<br />

does not necessarily exist on the server. And the other argument is a function, another live object instead<br />

<strong>of</strong> the kind <strong>of</strong> inert data structure that RPC mechanisms usually support.<br />

The server exposes a single method that takes the pr<strong>of</strong>fered file object and callable function. It uses<br />

these exactly as you would in a normal <strong>Python</strong> program that was happening inside a single process. It<br />

calls the file object’s readlines() and expects the return value to be an iterator over which a for loop can<br />

repeat. Finally, the server calls the function object that has been passed in without any regard for where<br />

the function actually lives (namely, in the client). Note that RPyC’s new security model dictates that,<br />

absent any special permission, it will only allow clients to call methods that start with the special prefix,<br />

exposed_.<br />

Listing 18–8. An RPyC Server<br />

#!/usr/bin/env python<br />

# <strong>Foundations</strong> <strong>of</strong> <strong>Python</strong> <strong>Network</strong> <strong>Programming</strong> - Chapter 18 - rpyc_server.py<br />

# RPyC server<br />

import rpyc<br />

class MyService(rpyc.Service):<br />

» def exposed_line_counter(self, fileobj, function):<br />

» » for linenum, line in enumerate(fileobj.readlines()):<br />

» » » function(line)<br />

» » return linenum + 1<br />

from rpyc.utils.server import ThreadedServer<br />

t = ThreadedServer(MyService, port = 18861)<br />

t.start()<br />

It is especially instructive to look at the output generated by running the client, assuming that a<br />

small testfile.txt indeed exists in the current directory and that it has a few words <strong>of</strong> wisdom inside:<br />

$ python rpyc_client.py<br />

Noisy: 'Simple\n'<br />

Noisy: 'is\n'<br />

Noisy: 'better\n'<br />

Noisy: 'than\n'<br />

Noisy: 'complex.\n'<br />

The number <strong>of</strong> lines in the file was 5<br />

Equally startling here are two facts. First, the server was able to iterate over multiple results from<br />

readlines(), even though this required the repeated invocation <strong>of</strong> file-object logic that lived on the<br />

client. Second, the server didn’t somehow copy the noisy() function’s code object so it could run the<br />

function directly; instead, it repeatedly invoked the function, with the correct argument each time, on<br />

the client side <strong>of</strong> the connection!<br />

How is this happening? Quite simply, RPyC takes exactly the opposite approach from the other RPC<br />

mechanisms we have looked at. Whereas all <strong>of</strong> the other techniques try to serialize and send as much<br />

information across the network as possible, and then leave the remote code to either succeed or fail with<br />

no further information from the client, the RPyC scheme only serializes completely immutable items<br />

318

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!