Chapter 18 ■ rpC
337
Running the preceding code against the example server produces output from which you can learn several things
about XML-RPC in particular and RPC mechanisms in general. Note how almost all of the calls work without a hitch
and how both of the calls in this listing and the functions themselves from Listing 18-1 look like completely normal
Python; there is nothing about them that is specific to a network:
$ python xmlrpc_client.py
xÿz
55
[0.0, 8.0]
[-1.0]
[1, 2.0, 'three']
[1, 2.0, 'three']
{'data': {'age': [42], 'sex': 'M'}, 'name': 'Arthur'}
Traceback (most recent call last):
...
xmlrpclib.Fault: <Fault 1: "<type 'exceptions.ValueError'>:math domain error">
But there are several details to which you will want to pay attention. First, note that XML-RPC is not imposing
any restrictions on the argument types you are supplying. You can call addtogether() with either strings or numbers,
and you can supply any number of arguments. The protocol itself does not care; it has no preconceived notion of how
many arguments a function should take or what its types should be. Of course, if you were making calls to a language
that did care—or even to a Python function that did not support variable-length argument lists—then the remote
language could raise an exception. But that would be the language complaining, not the XML-RPC protocol itself.
Second, note that XML-RPC function calls, like those of Python and many other languages in its lineage, can take
several arguments, but they can only return a single result value. That value might be a complex data structure, but it
will be returned as a single result. And the protocol does not care whether that result has a consistent shape or size; the
list returned by quadratic() (yes, I was tired of all of the simple add() and subtract() math functions that tend to get
used in XML-RPC examples!) varies in the number of elements returned without any complaint from the network logic.
Third, note that the rich variety of Python data types must be reduced to the smaller set that XML-RPC itself
happens to support. In particular, XML-RPC only supports a single sequence type: the list. So when you supply
remote_repr() with a tuple of three items, it is actually a list of three items that gets received at the server. This is a
common feature of all RPC mechanisms when they are coupled with a particular language. Types they do not directly
support either have to be mapped to a different data structure (as the tuple was here turned into a list) or an exception
has to be raised complaining that a particular argument type cannot be transmitted.
Fourth, complex data structures in XML-RPC can be recursive. You are not restricted to arguments that have only
one level of complex data type inside. Passing a dictionary with another dictionary as one of its values works just fine,
as you can see.
Finally, note that, as promised earlier, an exception in the function on the server made it successfully back across
the network and was represented locally on the client by an xmlrpclib.Fault instance. This instance provided the
remote exception name and the error message associated with it. Whatever language was used to implement the
server routines, you can always expect XML-RPC exceptions to have this structure. The traceback is not terribly
informative; while it tells you which call in the code triggered the exception, the innermost levels of the stack are
simply the code of the xmlrpclib itself.
Thus far I’ve covered the general features and restrictions of XML-RPC. If you consult the documentation for
either the client or the server module in the Python Standard Library, you can learn about a few more features. In
particular, you can learn how to use TLS and authentication by supplying more arguments to the ServerProxy class.
But one feature is important enough to cover here: the ability to make several calls in a network roundtrip when the
server supports it (it is another one of those optional extensions), as shown in Listing 18-4.