ActiveState Powered by ActiveState

Recipe 81549: A simple XML-RPC server


This recipe demonstrates the creation of a simple XML-RPC server using the SimpleXMLRPCServer class. It requires either Python 2.2 or later or the XML-RPC package from PythonWare (http://www.pythonware.com/products/xmlrpc/index.htm) to run.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Server code

import SimpleXMLRPCServer

class StringFunctions:
    def __init__(self):
        # Make all of the Python string functions available through
        # python_string.func_name
        import string
        self.python_string = string

    def _privateFunction(self):
        # This function cannot be called through XML-RPC because it
        # starts with an '_'
        pass
    
    def chop_in_half(self, astr):
        return astr[:len(astr)/2]

    def repeat(self, astr, times):
        return astr * times
    
server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 8000))
server.register_instance(StringFunctions())
server.register_function(lambda astr: '_' + astr, '_string')
server.serve_forever()

# Client code

import xmlrpclib

server = xmlrpclib.Server('http://localhost:8000')
print server.chop_in_half('I am a confidant guy')
print server.repeat('Repetition is the key to learning!\n', 5)
print server._string('<= underscore')
print server.python_string.join(['I', 'like it!'], " don't ")
print server._privateFunction() # Will throw an exception

Discussion

SimpleXMLRPCServer is a simple class that listens for HTTP requests, on a specified port, and dispatches any XML-RPC calls to either a registered instance or a registered function. The example above demonstates both usages.

Registering functions (as opposed to an instance) is necessary if you want to give your functions names beginning with '_' or containing characters not allowed in Python identifiers (Unicode characters, plus signs, etc.)

Note that dotted names (i.e. python_string.join) are correctly resolved for registered instances.

Comments

  1. 1. At 10:11 a.m. on 22 jun 2004, Duncan Gough said:

    Threaded example. This isn't my code, I found it when I wanted to run an XMLRPC server in a separate thread:

    class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
            pass
    
    if __name__ == "__main__":
            server = SimpleThreadedXMLRPCServer(("localhost", 8001))
            server.register_instance(MyXMLRPCObject()) # register your distant Object here
            server.serve_forever()
    

    More here:

    http://www.codecomments.com/Python/message190478.html

    http://www.python.org/doc/2.3.3/lib/module-SocketServer.html

  2. 2. At 6:29 a.m. on 13 jan 2005, A.M. Kuchling said:

    Forking XML-RPC server. Similarly, you can invoke each XML-RPC method in a single process with code like this:

    import SocketServer
    import SimpleXMLRPCServer
    
    class ForkingXMLRPCServer (SocketServer.ForkingMixIn,
                               SimpleXMLRPCServer.SimpleXMLRPCServer):
        pass
    
    server = ForkingXMLRPCServer(("localhost", 8000))
    # Register functions here...
    server.serve_forever()
    

    If using this code, remember that the methods can't rely on any shared global state, for example such as a dictionary used as a cache, because each method is run in a subprocess that does its work and then exits. Any changes to global variables or object attributes in the method won't be visible to the parent process.

  3. 3. At 9:25 p.m. on 29 apr 2006, KAMOSAWA Masao said:

    2.4 doesn't allow dotted notation of functions. In this server code, __init__ has

        import string
        self.python_string = string
    

    lines and the client code has the

    print server.python_string.join(['I', 'like it!'], " don't ")

    line. These codes are intended to use the dotted notation of function that Python 2.4's xmlrpclib disallows to use in default so the client code raise an exception when the line is executed. You can choise from some options to make the client code line to work. they are: ・assign string functions FUNCNAME to self.python_string.FUNCNAME one bye one or by looping names of string functions. ・change the server code: server.register_instance(StringFunctions()) to: server.register_instance(StringFunctions(), allow_dotted_names = True) allows dotted notation of the function.

  4. 4. At 3:55 a.m. on 18 jul 2007, Thomas Herchenroeder said:

    Error Message. This is just to confirm the previous post, and add the exception error message (so people like me find this comment faster through searches) if you use the recipe literally :

    xmlrpclib.Fault: &lt;Fault 1: '&lt;type \'exceptions.Exception\'&gt;:method "python_string.join" is not supported'&gt;
    

    =Thomas

  5. 5. At 4:09 a.m. on 23 jul 2007, Gal Aviel said:

    compression. (Disclaimer : I'm not a python expert in any way, just sharing...)

    xmlrpc is great, easy, dynamic but is not effecient as binary methods (see "The Python Web services developer: Messaging technologies compared" at http://www.ibm.com/developerworks/library/ws-pyth9/).

    For example, it can take minutes to transfer 100MB of data between client and server.

    Therefore, I added compression, using http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/355486.

    This shortened the transfer time to 15 seconds. Example server function (returns an arbitrary chunk of bytes, possibly with random values):

    from array import array
    from random import randint
    import zlib, base64
    
        def get_data(self, default_byte_value=0x00,num_bytes=1024,use_rand=False):
            data = array('B', [default_byte_value] * num_bytes )
            if ( use_rand ):
                for i in range(num_bytes):
                    data[i] = randint(0, 255)
            data,csum = asciiCompress(buffer(data))
            return data
    

    Example client side (request a 1MB chunk of bytes using 0x11 as default value for each byte (non random)).

    data = server.get_data(0x11,1 * 10**6,False)
    data,csum = asciiDecompress(data)
    arr = array('B',data)
    

    Note that we originate from a python array at the server and end up with an array at the client.

    Just my 2 cents :)

Sign in to comment