PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/lib-python/2.7/DocXMLRPCServer.py

https://bitbucket.org/kkris/pypy
Python | 279 lines | 192 code | 32 blank | 55 comment | 14 complexity | 316d0151242864239a2485bfb6155b3d MD5 | raw file
  1. """Self documenting XML-RPC Server.
  2. This module can be used to create XML-RPC servers that
  3. serve pydoc-style documentation in response to HTTP
  4. GET requests. This documentation is dynamically generated
  5. based on the functions and methods registered with the
  6. server.
  7. This module is built upon the pydoc and SimpleXMLRPCServer
  8. modules.
  9. """
  10. import pydoc
  11. import inspect
  12. import re
  13. import sys
  14. from SimpleXMLRPCServer import (SimpleXMLRPCServer,
  15. SimpleXMLRPCRequestHandler,
  16. CGIXMLRPCRequestHandler,
  17. resolve_dotted_attribute)
  18. class ServerHTMLDoc(pydoc.HTMLDoc):
  19. """Class used to generate pydoc HTML document for a server"""
  20. def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
  21. """Mark up some plain text, given a context of symbols to look for.
  22. Each context dictionary maps object names to anchor names."""
  23. escape = escape or self.escape
  24. results = []
  25. here = 0
  26. # XXX Note that this regular expression does not allow for the
  27. # hyperlinking of arbitrary strings being used as method
  28. # names. Only methods with names consisting of word characters
  29. # and '.'s are hyperlinked.
  30. pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
  31. r'RFC[- ]?(\d+)|'
  32. r'PEP[- ]?(\d+)|'
  33. r'(self\.)?((?:\w|\.)+))\b')
  34. while 1:
  35. match = pattern.search(text, here)
  36. if not match: break
  37. start, end = match.span()
  38. results.append(escape(text[here:start]))
  39. all, scheme, rfc, pep, selfdot, name = match.groups()
  40. if scheme:
  41. url = escape(all).replace('"', '"')
  42. results.append('<a href="%s">%s</a>' % (url, url))
  43. elif rfc:
  44. url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
  45. results.append('<a href="%s">%s</a>' % (url, escape(all)))
  46. elif pep:
  47. url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
  48. results.append('<a href="%s">%s</a>' % (url, escape(all)))
  49. elif text[end:end+1] == '(':
  50. results.append(self.namelink(name, methods, funcs, classes))
  51. elif selfdot:
  52. results.append('self.<strong>%s</strong>' % name)
  53. else:
  54. results.append(self.namelink(name, classes))
  55. here = end
  56. results.append(escape(text[here:]))
  57. return ''.join(results)
  58. def docroutine(self, object, name, mod=None,
  59. funcs={}, classes={}, methods={}, cl=None):
  60. """Produce HTML documentation for a function or method object."""
  61. anchor = (cl and cl.__name__ or '') + '-' + name
  62. note = ''
  63. title = '<a name="%s"><strong>%s</strong></a>' % (
  64. self.escape(anchor), self.escape(name))
  65. if inspect.ismethod(object):
  66. args, varargs, varkw, defaults = inspect.getargspec(object.im_func)
  67. # exclude the argument bound to the instance, it will be
  68. # confusing to the non-Python user
  69. argspec = inspect.formatargspec (
  70. args[1:],
  71. varargs,
  72. varkw,
  73. defaults,
  74. formatvalue=self.formatvalue
  75. )
  76. elif inspect.isfunction(object):
  77. args, varargs, varkw, defaults = inspect.getargspec(object)
  78. argspec = inspect.formatargspec(
  79. args, varargs, varkw, defaults, formatvalue=self.formatvalue)
  80. else:
  81. argspec = '(...)'
  82. if isinstance(object, tuple):
  83. argspec = object[0] or argspec
  84. docstring = object[1] or ""
  85. else:
  86. docstring = pydoc.getdoc(object)
  87. decl = title + argspec + (note and self.grey(
  88. '<font face="helvetica, arial">%s</font>' % note))
  89. doc = self.markup(
  90. docstring, self.preformat, funcs, classes, methods)
  91. doc = doc and '<dd><tt>%s</tt></dd>' % doc
  92. return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)
  93. def docserver(self, server_name, package_documentation, methods):
  94. """Produce HTML documentation for an XML-RPC server."""
  95. fdict = {}
  96. for key, value in methods.items():
  97. fdict[key] = '#-' + key
  98. fdict[value] = fdict[key]
  99. server_name = self.escape(server_name)
  100. head = '<big><big><strong>%s</strong></big></big>' % server_name
  101. result = self.heading(head, '#ffffff', '#7799ee')
  102. doc = self.markup(package_documentation, self.preformat, fdict)
  103. doc = doc and '<tt>%s</tt>' % doc
  104. result = result + '<p>%s</p>\n' % doc
  105. contents = []
  106. method_items = sorted(methods.items())
  107. for key, value in method_items:
  108. contents.append(self.docroutine(value, key, funcs=fdict))
  109. result = result + self.bigsection(
  110. 'Methods', '#ffffff', '#eeaa77', pydoc.join(contents))
  111. return result
  112. class XMLRPCDocGenerator:
  113. """Generates documentation for an XML-RPC server.
  114. This class is designed as mix-in and should not
  115. be constructed directly.
  116. """
  117. def __init__(self):
  118. # setup variables used for HTML documentation
  119. self.server_name = 'XML-RPC Server Documentation'
  120. self.server_documentation = \
  121. "This server exports the following methods through the XML-RPC "\
  122. "protocol."
  123. self.server_title = 'XML-RPC Server Documentation'
  124. def set_server_title(self, server_title):
  125. """Set the HTML title of the generated server documentation"""
  126. self.server_title = server_title
  127. def set_server_name(self, server_name):
  128. """Set the name of the generated HTML server documentation"""
  129. self.server_name = server_name
  130. def set_server_documentation(self, server_documentation):
  131. """Set the documentation string for the entire server."""
  132. self.server_documentation = server_documentation
  133. def generate_html_documentation(self):
  134. """generate_html_documentation() => html documentation for the server
  135. Generates HTML documentation for the server using introspection for
  136. installed functions and instances that do not implement the
  137. _dispatch method. Alternatively, instances can choose to implement
  138. the _get_method_argstring(method_name) method to provide the
  139. argument string used in the documentation and the
  140. _methodHelp(method_name) method to provide the help text used
  141. in the documentation."""
  142. methods = {}
  143. for method_name in self.system_listMethods():
  144. if method_name in self.funcs:
  145. method = self.funcs[method_name]
  146. elif self.instance is not None:
  147. method_info = [None, None] # argspec, documentation
  148. if hasattr(self.instance, '_get_method_argstring'):
  149. method_info[0] = self.instance._get_method_argstring(method_name)
  150. if hasattr(self.instance, '_methodHelp'):
  151. method_info[1] = self.instance._methodHelp(method_name)
  152. method_info = tuple(method_info)
  153. if method_info != (None, None):
  154. method = method_info
  155. elif not hasattr(self.instance, '_dispatch'):
  156. try:
  157. method = resolve_dotted_attribute(
  158. self.instance,
  159. method_name
  160. )
  161. except AttributeError:
  162. method = method_info
  163. else:
  164. method = method_info
  165. else:
  166. assert 0, "Could not find method in self.functions and no "\
  167. "instance installed"
  168. methods[method_name] = method
  169. documenter = ServerHTMLDoc()
  170. documentation = documenter.docserver(
  171. self.server_name,
  172. self.server_documentation,
  173. methods
  174. )
  175. return documenter.page(self.server_title, documentation)
  176. class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
  177. """XML-RPC and documentation request handler class.
  178. Handles all HTTP POST requests and attempts to decode them as
  179. XML-RPC requests.
  180. Handles all HTTP GET requests and interprets them as requests
  181. for documentation.
  182. """
  183. def do_GET(self):
  184. """Handles the HTTP GET request.
  185. Interpret all HTTP GET requests as requests for server
  186. documentation.
  187. """
  188. # Check that the path is legal
  189. if not self.is_rpc_path_valid():
  190. self.report_404()
  191. return
  192. response = self.server.generate_html_documentation()
  193. self.send_response(200)
  194. self.send_header("Content-type", "text/html")
  195. self.send_header("Content-length", str(len(response)))
  196. self.end_headers()
  197. self.wfile.write(response)
  198. class DocXMLRPCServer( SimpleXMLRPCServer,
  199. XMLRPCDocGenerator):
  200. """XML-RPC and HTML documentation server.
  201. Adds the ability to serve server documentation to the capabilities
  202. of SimpleXMLRPCServer.
  203. """
  204. def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
  205. logRequests=1, allow_none=False, encoding=None,
  206. bind_and_activate=True):
  207. SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests,
  208. allow_none, encoding, bind_and_activate)
  209. XMLRPCDocGenerator.__init__(self)
  210. class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler,
  211. XMLRPCDocGenerator):
  212. """Handler for XML-RPC data and documentation requests passed through
  213. CGI"""
  214. def handle_get(self):
  215. """Handles the HTTP GET request.
  216. Interpret all HTTP GET requests as requests for server
  217. documentation.
  218. """
  219. response = self.generate_html_documentation()
  220. print 'Content-Type: text/html'
  221. print 'Content-Length: %d' % len(response)
  222. print
  223. sys.stdout.write(response)
  224. def __init__(self):
  225. CGIXMLRPCRequestHandler.__init__(self)
  226. XMLRPCDocGenerator.__init__(self)