This is an "automatic DocXmlRpcServer." There is also a CGI variant: AutoXmlRpcCgi.

<<TableOfContents>>

== Tutorial ==

Suppose you have a Python module:

{{{
#!python
def spam():
    return "spam"
}}}

How can you share that on [[http://www.irchelp.org/|IRC?]]

Mark it like this:
{{{
#!python
XMLRPC_namespace = "eggs"

def spam():
    return "spam"
}}}

Now, just run the AutoXmlRpcServer from that directory, and you're done!

{{{
$ ./xrserver.py
Sat Apr 16 21:29:24 2005 Application Starting.
}}}

You're XML-RPC server's up and running, port 8000.

Your friends can now call your function!

{{{
#!python
import xmlrpclib

server = xmlrpclib.ServerProxy("http://example.com:8000/")
print server.eggs.spam()
}}}

There it is!

== Notes ==

 * What if you can't run a server? Use AutoXmlRpcCgi!
 * You can change the host and portname. Run "./xrserver --help" for options.
 * If you set XMLRPC_namespace to None, then namespaces aren't used.
 * This code demonstrates ModulesAsPlugins, DocXmlRpcServer, OptParse, and (hopefully) PythonStyle.
 * If you define a function "uli" ({{{def uli(msg):}}}), you can call it in IRC with [[http://onebigsoup.wiki.taoriver.net/moin.cgi/UliBot|UliBot!]]

== Code: xrserver.py ==

{{{
#!python
#!/usr/bin/env python
"""Serve specially marked modules by XML-RPC.

  -Hhostname   host name, default ""
  -Pportnum    port number, default 8000

This script starts an XML-RPC server, and publishes auto-detected
modules from the working directory.

Functions within Modules that define the name "XMLRPC_namespace" are
published. Function names that begin with an underscore (ex: _eggs) are
not published. Functions are published within the XML-RPC namespace
designated by the XMLRPC_namespace value, or the base namespace if the
value is None.
"""

import time
import os
import imp
import types

import optparse
import DocXMLRPCServer


def find_modules(path="."):
    """Return names of modules in a directory.

    Returns module names in a list. Filenames that end in ".py" or
    ".pyc" are considered to be modules. The extension is not included
    in the returned list.
    """
    modules = set()
    for filename in os.listdir(path):
        module = None
        if filename.endswith(".py"):
            module = filename[:-3]
        elif filename.endswith(".pyc"):
            module = filename[:-4]
        if module is not None:
            modules.add(module)
    return list(modules)


def load_module(name, path=["."]):
    """Return a named module found in a given path."""
    (file, pathname, description) = imp.find_module(name, path)
    return imp.load_module(name, file, pathname, description)


def find_xmlrpc_modules():
    """Find modules that define XMLRPC_namespace.

    Loads all modules in the current working directory. Returns a list
    of modules, the modules that define XMLRPC_namespace.
    """
    modules = [load_module(m) for m in find_modules()]
    xmlrpc_modules = []
    for m in modules:
        if m.__dict__.has_key("XMLRPC_namespace"):
            xmlrpc_modules.append(m)
    return xmlrpc_modules


def functions_in_module(module):
    """Find all functions in a module."""
    functions = []
    for obj in module.__dict__.values():
        if isinstance(obj, types.FunctionType):
            functions.append(obj)
    return functions


if __name__ == "__main__":
    parser = optparse.OptionParser(__doc__)
    parser.add_option("-H", "--host", dest="hostname",
                      default="127.0.0.1", type="string",
                      help="specify hostname to run on")
    parser.add_option("-p", "--port", dest="portnum", default=8000,
                      type="int", help="port number to run on")

    (options, args) = parser.parse_args()
    if len(args) != 0:
        parser.error("incorrect number of arguments")

    ServerClass = DocXMLRPCServer.DocXMLRPCServer
    server = ServerClass((options.hostname, options.portnum),
                         logRequests=0)

    for module in find_xmlrpc_modules():
        for func in functions_in_module(module):
            if func.__name__.startswith("_"):
                continue
            full_name = func.__name__
            if module.XMLRPC_namespace is not None:
                full_name = "%s.%s" % (module.XMLRPC_namespace,
                                       full_name)
            server.register_function(func, full_name)

    server.set_server_title("xrserver")
    server.register_introspection_functions()

    print time.asctime(), 'Application Starting.'
    server.serve_forever()
    print time.asctime(), 'Application Finishing.'
}}}

= Discussion =

This could be improved. Some ideas:

 * Make it so you can specify modules (and possibly namespaces for them) with command line options. (hint: OptParse.)
 * What if there's an exception while loading a module? What then?
 * Respond gracefully to CTRL-C.
 * Log modules successfully loaded.
    * If you're either brave or insane, make use of the LoggingModule.

-- LionKimbro <<DateTime(2005-04-17T05:51:41Z)>>