home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 June / PCWorld_2005-06_cd.bin / software / vyzkuste / firewally / firewally.exe / framework-2.3.exe / DocXMLRPCServer.py < prev    next >
Text File  |  2003-12-30  |  11KB  |  304 lines

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