python-2.5.2/win32/Lib/DocXMLRPCServer.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 """Self documenting XML-RPC Server.
       
     2 
       
     3 This module can be used to create XML-RPC servers that
       
     4 serve pydoc-style documentation in response to HTTP
       
     5 GET requests. This documentation is dynamically generated
       
     6 based on the functions and methods registered with the
       
     7 server.
       
     8 
       
     9 This module is built upon the pydoc and SimpleXMLRPCServer
       
    10 modules.
       
    11 """
       
    12 
       
    13 import pydoc
       
    14 import inspect
       
    15 import re
       
    16 import sys
       
    17 
       
    18 from SimpleXMLRPCServer import (SimpleXMLRPCServer,
       
    19             SimpleXMLRPCRequestHandler,
       
    20             CGIXMLRPCRequestHandler,
       
    21             resolve_dotted_attribute)
       
    22 
       
    23 class ServerHTMLDoc(pydoc.HTMLDoc):
       
    24     """Class used to generate pydoc HTML document for a server"""
       
    25 
       
    26     def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
       
    27         """Mark up some plain text, given a context of symbols to look for.
       
    28         Each context dictionary maps object names to anchor names."""
       
    29         escape = escape or self.escape
       
    30         results = []
       
    31         here = 0
       
    32 
       
    33         # XXX Note that this regular expressions does not allow for the
       
    34         # hyperlinking of arbitrary strings being used as method
       
    35         # names. Only methods with names consisting of word characters
       
    36         # and '.'s are hyperlinked.
       
    37         pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
       
    38                                 r'RFC[- ]?(\d+)|'
       
    39                                 r'PEP[- ]?(\d+)|'
       
    40                                 r'(self\.)?((?:\w|\.)+))\b')
       
    41         while 1:
       
    42             match = pattern.search(text, here)
       
    43             if not match: break
       
    44             start, end = match.span()
       
    45             results.append(escape(text[here:start]))
       
    46 
       
    47             all, scheme, rfc, pep, selfdot, name = match.groups()
       
    48             if scheme:
       
    49                 url = escape(all).replace('"', '"')
       
    50                 results.append('<a href="%s">%s</a>' % (url, url))
       
    51             elif rfc:
       
    52                 url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
       
    53                 results.append('<a href="%s">%s</a>' % (url, escape(all)))
       
    54             elif pep:
       
    55                 url = 'http://www.python.org/peps/pep-%04d.html' % int(pep)
       
    56                 results.append('<a href="%s">%s</a>' % (url, escape(all)))
       
    57             elif text[end:end+1] == '(':
       
    58                 results.append(self.namelink(name, methods, funcs, classes))
       
    59             elif selfdot:
       
    60                 results.append('self.<strong>%s</strong>' % name)
       
    61             else:
       
    62                 results.append(self.namelink(name, classes))
       
    63             here = end
       
    64         results.append(escape(text[here:]))
       
    65         return ''.join(results)
       
    66 
       
    67     def docroutine(self, object, name=None, mod=None,
       
    68                    funcs={}, classes={}, methods={}, cl=None):
       
    69         """Produce HTML documentation for a function or method object."""
       
    70 
       
    71         anchor = (cl and cl.__name__ or '') + '-' + name
       
    72         note = ''
       
    73 
       
    74         title = '<a name="%s"><strong>%s</strong></a>' % (anchor, name)
       
    75 
       
    76         if inspect.ismethod(object):
       
    77             args, varargs, varkw, defaults = inspect.getargspec(object.im_func)
       
    78             # exclude the argument bound to the instance, it will be
       
    79             # confusing to the non-Python user
       
    80             argspec = inspect.formatargspec (
       
    81                     args[1:],
       
    82                     varargs,
       
    83                     varkw,
       
    84                     defaults,
       
    85                     formatvalue=self.formatvalue
       
    86                 )
       
    87         elif inspect.isfunction(object):
       
    88             args, varargs, varkw, defaults = inspect.getargspec(object)
       
    89             argspec = inspect.formatargspec(
       
    90                 args, varargs, varkw, defaults, formatvalue=self.formatvalue)
       
    91         else:
       
    92             argspec = '(...)'
       
    93 
       
    94         if isinstance(object, tuple):
       
    95             argspec = object[0] or argspec
       
    96             docstring = object[1] or ""
       
    97         else:
       
    98             docstring = pydoc.getdoc(object)
       
    99 
       
   100         decl = title + argspec + (note and self.grey(
       
   101                '<font face="helvetica, arial">%s</font>' % note))
       
   102 
       
   103         doc = self.markup(
       
   104             docstring, self.preformat, funcs, classes, methods)
       
   105         doc = doc and '<dd><tt>%s</tt></dd>' % doc
       
   106         return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
       
   107 
       
   108     def docserver(self, server_name, package_documentation, methods):
       
   109         """Produce HTML documentation for an XML-RPC server."""
       
   110 
       
   111         fdict = {}
       
   112         for key, value in methods.items():
       
   113             fdict[key] = '#-' + key
       
   114             fdict[value] = fdict[key]
       
   115 
       
   116         head = '<big><big><strong>%s</strong></big></big>' % server_name
       
   117         result = self.heading(head, '#ffffff', '#7799ee')
       
   118 
       
   119         doc = self.markup(package_documentation, self.preformat, fdict)
       
   120         doc = doc and '<tt>%s</tt>' % doc
       
   121         result = result + '<p>%s</p>\n' % doc
       
   122 
       
   123         contents = []
       
   124         method_items = methods.items()
       
   125         method_items.sort()
       
   126         for key, value in method_items:
       
   127             contents.append(self.docroutine(value, key, funcs=fdict))
       
   128         result = result + self.bigsection(
       
   129             'Methods', '#ffffff', '#eeaa77', pydoc.join(contents))
       
   130 
       
   131         return result
       
   132 
       
   133 class XMLRPCDocGenerator:
       
   134     """Generates documentation for an XML-RPC server.
       
   135 
       
   136     This class is designed as mix-in and should not
       
   137     be constructed directly.
       
   138     """
       
   139 
       
   140     def __init__(self):
       
   141         # setup variables used for HTML documentation
       
   142         self.server_name = 'XML-RPC Server Documentation'
       
   143         self.server_documentation = \
       
   144             "This server exports the following methods through the XML-RPC "\
       
   145             "protocol."
       
   146         self.server_title = 'XML-RPC Server Documentation'
       
   147 
       
   148     def set_server_title(self, server_title):
       
   149         """Set the HTML title of the generated server documentation"""
       
   150 
       
   151         self.server_title = server_title
       
   152 
       
   153     def set_server_name(self, server_name):
       
   154         """Set the name of the generated HTML server documentation"""
       
   155 
       
   156         self.server_name = server_name
       
   157 
       
   158     def set_server_documentation(self, server_documentation):
       
   159         """Set the documentation string for the entire server."""
       
   160 
       
   161         self.server_documentation = server_documentation
       
   162 
       
   163     def generate_html_documentation(self):
       
   164         """generate_html_documentation() => html documentation for the server
       
   165 
       
   166         Generates HTML documentation for the server using introspection for
       
   167         installed functions and instances that do not implement the
       
   168         _dispatch method. Alternatively, instances can choose to implement
       
   169         the _get_method_argstring(method_name) method to provide the
       
   170         argument string used in the documentation and the
       
   171         _methodHelp(method_name) method to provide the help text used
       
   172         in the documentation."""
       
   173 
       
   174         methods = {}
       
   175 
       
   176         for method_name in self.system_listMethods():
       
   177             if self.funcs.has_key(method_name):
       
   178                 method = self.funcs[method_name]
       
   179             elif self.instance is not None:
       
   180                 method_info = [None, None] # argspec, documentation
       
   181                 if hasattr(self.instance, '_get_method_argstring'):
       
   182                     method_info[0] = self.instance._get_method_argstring(method_name)
       
   183                 if hasattr(self.instance, '_methodHelp'):
       
   184                     method_info[1] = self.instance._methodHelp(method_name)
       
   185 
       
   186                 method_info = tuple(method_info)
       
   187                 if method_info != (None, None):
       
   188                     method = method_info
       
   189                 elif not hasattr(self.instance, '_dispatch'):
       
   190                     try:
       
   191                         method = resolve_dotted_attribute(
       
   192                                     self.instance,
       
   193                                     method_name
       
   194                                     )
       
   195                     except AttributeError:
       
   196                         method = method_info
       
   197                 else:
       
   198                     method = method_info
       
   199             else:
       
   200                 assert 0, "Could not find method in self.functions and no "\
       
   201                           "instance installed"
       
   202 
       
   203             methods[method_name] = method
       
   204 
       
   205         documenter = ServerHTMLDoc()
       
   206         documentation = documenter.docserver(
       
   207                                 self.server_name,
       
   208                                 self.server_documentation,
       
   209                                 methods
       
   210                             )
       
   211 
       
   212         return documenter.page(self.server_title, documentation)
       
   213 
       
   214 class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
       
   215     """XML-RPC and documentation request handler class.
       
   216 
       
   217     Handles all HTTP POST requests and attempts to decode them as
       
   218     XML-RPC requests.
       
   219 
       
   220     Handles all HTTP GET requests and interprets them as requests
       
   221     for documentation.
       
   222     """
       
   223 
       
   224     def do_GET(self):
       
   225         """Handles the HTTP GET request.
       
   226 
       
   227         Interpret all HTTP GET requests as requests for server
       
   228         documentation.
       
   229         """
       
   230         # Check that the path is legal
       
   231         if not self.is_rpc_path_valid():
       
   232             self.report_404()
       
   233             return
       
   234 
       
   235         response = self.server.generate_html_documentation()
       
   236         self.send_response(200)
       
   237         self.send_header("Content-type", "text/html")
       
   238         self.send_header("Content-length", str(len(response)))
       
   239         self.end_headers()
       
   240         self.wfile.write(response)
       
   241 
       
   242         # shut down the connection
       
   243         self.wfile.flush()
       
   244         self.connection.shutdown(1)
       
   245 
       
   246 class DocXMLRPCServer(  SimpleXMLRPCServer,
       
   247                         XMLRPCDocGenerator):
       
   248     """XML-RPC and HTML documentation server.
       
   249 
       
   250     Adds the ability to serve server documentation to the capabilities
       
   251     of SimpleXMLRPCServer.
       
   252     """
       
   253 
       
   254     def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
       
   255                  logRequests=1):
       
   256         SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests)
       
   257         XMLRPCDocGenerator.__init__(self)
       
   258 
       
   259 class DocCGIXMLRPCRequestHandler(   CGIXMLRPCRequestHandler,
       
   260                                     XMLRPCDocGenerator):
       
   261     """Handler for XML-RPC data and documentation requests passed through
       
   262     CGI"""
       
   263 
       
   264     def handle_get(self):
       
   265         """Handles the HTTP GET request.
       
   266 
       
   267         Interpret all HTTP GET requests as requests for server
       
   268         documentation.
       
   269         """
       
   270 
       
   271         response = self.generate_html_documentation()
       
   272 
       
   273         print 'Content-Type: text/html'
       
   274         print 'Content-Length: %d' % len(response)
       
   275         print
       
   276         sys.stdout.write(response)
       
   277 
       
   278     def __init__(self):
       
   279         CGIXMLRPCRequestHandler.__init__(self)
       
   280         XMLRPCDocGenerator.__init__(self)
       
   281 
       
   282 if __name__ == '__main__':
       
   283     def deg_to_rad(deg):
       
   284         """deg_to_rad(90) => 1.5707963267948966
       
   285 
       
   286         Converts an angle in degrees to an angle in radians"""
       
   287         import math
       
   288         return deg * math.pi / 180
       
   289 
       
   290     server = DocXMLRPCServer(("localhost", 8000))
       
   291 
       
   292     server.set_server_title("Math Server")
       
   293     server.set_server_name("Math XML-RPC Server")
       
   294     server.set_server_documentation("""This server supports various mathematical functions.
       
   295 
       
   296 You can use it from Python as follows:
       
   297 
       
   298 >>> from xmlrpclib import ServerProxy
       
   299 >>> s = ServerProxy("http://localhost:8000")
       
   300 >>> s.deg_to_rad(90.0)
       
   301 1.5707963267948966""")
       
   302 
       
   303     server.register_function(deg_to_rad)
       
   304     server.register_introspection_functions()
       
   305 
       
   306     server.serve_forever()