PyXR

c:\python24\lib \ DocXMLRPCServer.py



0001 """Self documenting XML-RPC Server.
0002 
0003 This module can be used to create XML-RPC servers that
0004 serve pydoc-style documentation in response to HTTP
0005 GET requests. This documentation is dynamically generated
0006 based on the functions and methods registered with the
0007 server.
0008 
0009 This module is built upon the pydoc and SimpleXMLRPCServer
0010 modules.
0011 """
0012 
0013 import pydoc
0014 import inspect
0015 import types
0016 import re
0017 import sys
0018 
0019 from SimpleXMLRPCServer import (SimpleXMLRPCServer,
0020             SimpleXMLRPCRequestHandler,
0021             CGIXMLRPCRequestHandler,
0022             resolve_dotted_attribute)
0023 
0024 class ServerHTMLDoc(pydoc.HTMLDoc):
0025     """Class used to generate pydoc HTML document for a server"""
0026 
0027     def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
0028         """Mark up some plain text, given a context of symbols to look for.
0029         Each context dictionary maps object names to anchor names."""
0030         escape = escape or self.escape
0031         results = []
0032         here = 0
0033 
0034         # XXX Note that this regular expressions does not allow for the
0035         # hyperlinking of arbitrary strings being used as method
0036         # names. Only methods with names consisting of word characters
0037         # and '.'s are hyperlinked.
0038         pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
0039                                 r'RFC[- ]?(\d+)|'
0040                                 r'PEP[- ]?(\d+)|'
0041                                 r'(self\.)?((?:\w|\.)+))\b')
0042         while 1:
0043             match = pattern.search(text, here)
0044             if not match: break
0045             start, end = match.span()
0046             results.append(escape(text[here:start]))
0047 
0048             all, scheme, rfc, pep, selfdot, name = match.groups()
0049             if scheme:
0050                 url = escape(all).replace('"', '"')
0051                 results.append('<a href="%s">%s</a>' % (url, url))
0052             elif rfc:
0053                 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
0054                 results.append('<a href="%s">%s</a>' % (url, escape(all)))
0055             elif pep:
0056                 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
0057                 results.append('<a href="%s">%s</a>' % (url, escape(all)))
0058             elif text[end:end+1] == '(':
0059                 results.append(self.namelink(name, methods, funcs, classes))
0060             elif selfdot:
0061                 results.append('self.<strong>%s</strong>' % name)
0062             else:
0063                 results.append(self.namelink(name, classes))
0064             here = end
0065         results.append(escape(text[here:]))
0066         return ''.join(results)
0067 
0068     def docroutine(self, object, name=None, mod=None,
0069                    funcs={}, classes={}, methods={}, cl=None):
0070         """Produce HTML documentation for a function or method object."""
0071 
0072         anchor = (cl and cl.__name__ or '') + '-' + name
0073         note = ''
0074 
0075         title = '<a name="%s"><strong>%s</strong></a>' % (anchor, name)
0076 
0077         if inspect.ismethod(object):
0078             args, varargs, varkw, defaults = inspect.getargspec(object.im_func)
0079             # exclude the argument bound to the instance, it will be
0080             # confusing to the non-Python user
0081             argspec = inspect.formatargspec (
0082                     args[1:],
0083                     varargs,
0084                     varkw,
0085                     defaults,
0086                     formatvalue=self.formatvalue
0087                 )
0088         elif inspect.isfunction(object):
0089             args, varargs, varkw, defaults = inspect.getargspec(object)
0090             argspec = inspect.formatargspec(
0091                 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
0092         else:
0093             argspec = '(...)'
0094 
0095         if isinstance(object, types.TupleType):
0096             argspec = object[0] or argspec
0097             docstring = object[1] or ""
0098         else:
0099             docstring = pydoc.getdoc(object)
0100 
0101         decl = title + argspec + (note and self.grey(
0102                '<font face="helvetica, arial">%s</font>' % note))
0103 
0104         doc = self.markup(
0105             docstring, self.preformat, funcs, classes, methods)
0106         doc = doc and '<dd><tt>%s</tt></dd>' % doc
0107         return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
0108 
0109     def docserver(self, server_name, package_documentation, methods):
0110         """Produce HTML documentation for an XML-RPC server."""
0111 
0112         fdict = {}
0113         for key, value in methods.items():
0114             fdict[key] = '#-' + key
0115             fdict[value] = fdict[key]
0116 
0117         head = '<big><big><strong>%s</strong></big></big>' % server_name
0118         result = self.heading(head, '#ffffff', '#7799ee')
0119 
0120         doc = self.markup(package_documentation, self.preformat, fdict)
0121         doc = doc and '<tt>%s</tt>' % doc
0122         result = result + '<p>%s</p>\n' % doc
0123 
0124         contents = []
0125         method_items = methods.items()
0126         method_items.sort()
0127         for key, value in method_items:
0128             contents.append(self.docroutine(value, key, funcs=fdict))
0129         result = result + self.bigsection(
0130             'Methods', '#ffffff', '#eeaa77', pydoc.join(contents))
0131 
0132         return result
0133 
0134 class XMLRPCDocGenerator:
0135     """Generates documentation for an XML-RPC server.
0136 
0137     This class is designed as mix-in and should not
0138     be constructed directly.
0139     """
0140 
0141     def __init__(self):
0142         # setup variables used for HTML documentation
0143         self.server_name = 'XML-RPC Server Documentation'
0144         self.server_documentation = \
0145             "This server exports the following methods through the XML-RPC "\
0146             "protocol."
0147         self.server_title = 'XML-RPC Server Documentation'
0148 
0149     def set_server_title(self, server_title):
0150         """Set the HTML title of the generated server documentation"""
0151 
0152         self.server_title = server_title
0153 
0154     def set_server_name(self, server_name):
0155         """Set the name of the generated HTML server documentation"""
0156 
0157         self.server_name = server_name
0158 
0159     def set_server_documentation(self, server_documentation):
0160         """Set the documentation string for the entire server."""
0161 
0162         self.server_documentation = server_documentation
0163 
0164     def generate_html_documentation(self):
0165         """generate_html_documentation() => html documentation for the server
0166 
0167         Generates HTML documentation for the server using introspection for
0168         installed functions and instances that do not implement the
0169         _dispatch method. Alternatively, instances can choose to implement
0170         the _get_method_argstring(method_name) method to provide the
0171         argument string used in the documentation and the
0172         _methodHelp(method_name) method to provide the help text used
0173         in the documentation."""
0174 
0175         methods = {}
0176 
0177         for method_name in self.system_listMethods():
0178             if self.funcs.has_key(method_name):
0179                 method = self.funcs[method_name]
0180             elif self.instance is not None:
0181                 method_info = [None, None] # argspec, documentation
0182                 if hasattr(self.instance, '_get_method_argstring'):
0183                     method_info[0] = self.instance._get_method_argstring(method_name)
0184                 if hasattr(self.instance, '_methodHelp'):
0185                     method_info[1] = self.instance._methodHelp(method_name)
0186 
0187                 method_info = tuple(method_info)
0188                 if method_info != (None, None):
0189                     method = method_info
0190                 elif not hasattr(self.instance, '_dispatch'):
0191                     try:
0192                         method = resolve_dotted_attribute(
0193                                     self.instance,
0194                                     method_name
0195                                     )
0196                     except AttributeError:
0197                         method = method_info
0198                 else:
0199                     method = method_info
0200             else:
0201                 assert 0, "Could not find method in self.functions and no "\
0202                           "instance installed"
0203 
0204             methods[method_name] = method
0205 
0206         documenter = ServerHTMLDoc()
0207         documentation = documenter.docserver(
0208                                 self.server_name,
0209                                 self.server_documentation,
0210                                 methods
0211                             )
0212 
0213         return documenter.page(self.server_title, documentation)
0214 
0215 class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
0216     """XML-RPC and documentation request handler class.
0217 
0218     Handles all HTTP POST requests and attempts to decode them as
0219     XML-RPC requests.
0220 
0221     Handles all HTTP GET requests and interprets them as requests
0222     for documentation.
0223     """
0224 
0225     def do_GET(self):
0226         """Handles the HTTP GET request.
0227 
0228         Interpret all HTTP GET requests as requests for server
0229         documentation.
0230         """
0231 
0232         response = self.server.generate_html_documentation()
0233         self.send_response(200)
0234         self.send_header("Content-type", "text/html")
0235         self.send_header("Content-length", str(len(response)))
0236         self.end_headers()
0237         self.wfile.write(response)
0238 
0239         # shut down the connection
0240         self.wfile.flush()
0241         self.connection.shutdown(1)
0242 
0243 class DocXMLRPCServer(  SimpleXMLRPCServer,
0244                         XMLRPCDocGenerator):
0245     """XML-RPC and HTML documentation server.
0246 
0247     Adds the ability to serve server documentation to the capabilities
0248     of SimpleXMLRPCServer.
0249     """
0250 
0251     def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
0252                  logRequests=1):
0253         SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests)
0254         XMLRPCDocGenerator.__init__(self)
0255 
0256 class DocCGIXMLRPCRequestHandler(   CGIXMLRPCRequestHandler,
0257                                     XMLRPCDocGenerator):
0258     """Handler for XML-RPC data and documentation requests passed through
0259     CGI"""
0260 
0261     def handle_get(self):
0262         """Handles the HTTP GET request.
0263 
0264         Interpret all HTTP GET requests as requests for server
0265         documentation.
0266         """
0267 
0268         response = self.generate_html_documentation()
0269 
0270         print 'Content-Type: text/html'
0271         print 'Content-Length: %d' % len(response)
0272         print
0273         sys.stdout.write(response)
0274 
0275     def __init__(self):
0276         CGIXMLRPCRequestHandler.__init__(self)
0277         XMLRPCDocGenerator.__init__(self)
0278 
0279 if __name__ == '__main__':
0280     def deg_to_rad(deg):
0281         """deg_to_rad(90) => 1.5707963267948966
0282 
0283         Converts an angle in degrees to an angle in radians"""
0284         import math
0285         return deg * math.pi / 180
0286 
0287     server = DocXMLRPCServer(("localhost", 8000))
0288 
0289     server.set_server_title("Math Server")
0290     server.set_server_name("Math XML-RPC Server")
0291     server.set_server_documentation("""This server supports various mathematical functions.
0292 
0293 You can use it from Python as follows:
0294 
0295 >>> from xmlrpclib import ServerProxy
0296 >>> s = ServerProxy("http://localhost:8000")
0297 >>> s.deg_to_rad(90.0)
0298 1.5707963267948966""")
0299 
0300     server.register_function(deg_to_rad)
0301     server.register_introspection_functions()
0302 
0303     server.serve_forever()
0304 

Generated by PyXR 0.9.4
SourceForge.net Logo