diff options
-rw-r--r-- | Doc/library/docxmlrpcserver.rst | 95 | ||||
-rw-r--r-- | Doc/library/internet.rst | 5 | ||||
-rw-r--r-- | Doc/library/persistence.rst | 1 | ||||
-rw-r--r-- | Doc/library/xmlrpc.client.rst (renamed from Doc/library/xmlrpclib.rst) | 90 | ||||
-rw-r--r-- | Doc/library/xmlrpc.server.rst (renamed from Doc/library/simplexmlrpcserver.rst) | 113 | ||||
-rw-r--r-- | Doc/license.rst | 2 | ||||
-rw-r--r-- | Doc/tutorial/stdlib.rst | 2 | ||||
-rw-r--r-- | Lib/DocXMLRPCServer.py | 283 | ||||
-rw-r--r-- | Lib/test/test_anydbm.py | 147 | ||||
-rw-r--r-- | Lib/test/test_docxmlrpc.py | 3 | ||||
-rw-r--r-- | Lib/test/test_xmlrpc.py | 34 | ||||
-rw-r--r-- | Lib/test/test_xmlrpc_net.py | 2 | ||||
-rw-r--r-- | Lib/xmlrpc/__init__.py | 1 | ||||
-rw-r--r-- | Lib/xmlrpc/client.py (renamed from Lib/xmlrpclib.py) | 43 | ||||
-rw-r--r-- | Lib/xmlrpc/server.py (renamed from Lib/SimpleXMLRPCServer.py) | 295 | ||||
-rw-r--r-- | Misc/NEWS | 11 | ||||
-rw-r--r-- | Misc/cheatsheet | 3 |
17 files changed, 462 insertions, 668 deletions
diff --git a/Doc/library/docxmlrpcserver.rst b/Doc/library/docxmlrpcserver.rst deleted file mode 100644 index 8169684..0000000 --- a/Doc/library/docxmlrpcserver.rst +++ /dev/null @@ -1,95 +0,0 @@ - -:mod:`DocXMLRPCServer` --- Self-documenting XML-RPC server -========================================================== - -.. module:: DocXMLRPCServer - :synopsis: Self-documenting XML-RPC server implementation. -.. moduleauthor:: Brian Quinlan <brianq@activestate.com> -.. sectionauthor:: Brian Quinlan <brianq@activestate.com> - - -The :mod:`DocXMLRPCServer` module extends the classes found in -:mod:`SimpleXMLRPCServer` to serve HTML documentation in response to HTTP GET -requests. Servers can either be free standing, using :class:`DocXMLRPCServer`, -or embedded in a CGI environment, using :class:`DocCGIXMLRPCRequestHandler`. - - -.. class:: DocXMLRPCServer(addr[, requestHandler[, logRequests[, allow_none[, encoding[, bind_and_activate]]]]]) - - Create a new server instance. All parameters have the same meaning as for - :class:`SimpleXMLRPCServer.SimpleXMLRPCServer`; *requestHandler* defaults to - :class:`DocXMLRPCRequestHandler`. - - -.. class:: DocCGIXMLRPCRequestHandler() - - Create a new instance to handle XML-RPC requests in a CGI environment. - - -.. class:: DocXMLRPCRequestHandler() - - Create a new request handler instance. This request handler supports XML-RPC - POST requests, documentation GET requests, and modifies logging so that the - *logRequests* parameter to the :class:`DocXMLRPCServer` constructor parameter is - honored. - - -.. _doc-xmlrpc-servers: - -DocXMLRPCServer Objects ------------------------ - -The :class:`DocXMLRPCServer` class is derived from -:class:`SimpleXMLRPCServer.SimpleXMLRPCServer` and provides a means of creating -self-documenting, stand alone XML-RPC servers. HTTP POST requests are handled as -XML-RPC method calls. HTTP GET requests are handled by generating pydoc-style -HTML documentation. This allows a server to provide its own web-based -documentation. - - -.. method:: DocXMLRPCServer.set_server_title(server_title) - - Set the title used in the generated HTML documentation. This title will be used - inside the HTML "title" element. - - -.. method:: DocXMLRPCServer.set_server_name(server_name) - - Set the name used in the generated HTML documentation. This name will appear at - the top of the generated documentation inside a "h1" element. - - -.. method:: DocXMLRPCServer.set_server_documentation(server_documentation) - - Set the description used in the generated HTML documentation. This description - will appear as a paragraph, below the server name, in the documentation. - - -DocCGIXMLRPCRequestHandler --------------------------- - -The :class:`DocCGIXMLRPCRequestHandler` class is derived from -:class:`SimpleXMLRPCServer.CGIXMLRPCRequestHandler` and provides a means of -creating self-documenting, XML-RPC CGI scripts. HTTP POST requests are handled -as XML-RPC method calls. HTTP GET requests are handled by generating pydoc-style -HTML documentation. This allows a server to provide its own web-based -documentation. - - -.. method:: DocCGIXMLRPCRequestHandler.set_server_title(server_title) - - Set the title used in the generated HTML documentation. This title will be used - inside the HTML "title" element. - - -.. method:: DocCGIXMLRPCRequestHandler.set_server_name(server_name) - - Set the name used in the generated HTML documentation. This name will appear at - the top of the generated documentation inside a "h1" element. - - -.. method:: DocCGIXMLRPCRequestHandler.set_server_documentation(server_documentation) - - Set the description used in the generated HTML documentation. This description - will appear as a paragraph, below the server name, in the documentation. - diff --git a/Doc/library/internet.rst b/Doc/library/internet.rst index 16b0a44..a5f6d22 100644 --- a/Doc/library/internet.rst +++ b/Doc/library/internet.rst @@ -42,6 +42,5 @@ is currently supported on most popular platforms. Here is an overview: cgihttpserver.rst cookielib.rst cookie.rst - xmlrpclib.rst - simplexmlrpcserver.rst - docxmlrpcserver.rst + xmlrpc.client.rst + xmlrpc.server.rst diff --git a/Doc/library/persistence.rst b/Doc/library/persistence.rst index c5c2aa4..8d9fa3a 100644 --- a/Doc/library/persistence.rst +++ b/Doc/library/persistence.rst @@ -23,4 +23,5 @@ The list of modules described in this chapter is: shelve.rst marshal.rst dbm.rst + bsddb.rst sqlite3.rst diff --git a/Doc/library/xmlrpclib.rst b/Doc/library/xmlrpc.client.rst index c1f13c3..7d59750 100644 --- a/Doc/library/xmlrpclib.rst +++ b/Doc/library/xmlrpc.client.rst @@ -1,7 +1,7 @@ -:mod:`xmlrpclib` --- XML-RPC client access -========================================== +:mod:`xmlrpc.client` --- XML-RPC client access +============================================== -.. module:: xmlrpclib +.. module:: xmlrpc.client :synopsis: XML-RPC client access. .. moduleauthor:: Fredrik Lundh <fredrik@pythonware.com> .. sectionauthor:: Eric S. Raymond <esr@snark.thyrsus.com> @@ -86,7 +86,7 @@ between conformable Python objects and XML on the wire. raise a special :exc:`Fault` instance, used to signal XML-RPC server errors, or :exc:`ProtocolError` used to signal an error in the HTTP/HTTPS transport layer. Both :exc:`Fault` and :exc:`ProtocolError` derive from a base class called - :exc:`Error`. Note that the xmlrpclib module currently does not marshal + :exc:`Error`. Note that the xmlrpc client module currently does not marshal instances of subclasses of builtin types. When passing strings, characters special to XML such as ``<``, ``>``, and ``&`` @@ -169,28 +169,9 @@ grouped under the reserved :attr:`system` member: string may contain HTML markup. -.. _boolean-objects: - -Boolean Objects ---------------- - -This class may be initialized from any Python value; the instance returned -depends only on its truth value. It supports various Python operators through -:meth:`__cmp__`, :meth:`__repr__`, :meth:`__int__`, and :meth:`__bool__` -methods, all implemented in the obvious ways. - -It also has the following method, supported mainly for internal use by the -unmarshalling code: - - -.. method:: Boolean.encode(out) - - Write the XML-RPC encoding of this Boolean item to the out stream object. - A working example follows. The server code:: - import xmlrpclib - from SimpleXMLRPCServer import SimpleXMLRPCServer + from xmlrpc.server import SimpleXMLRPCServer def is_even(n): return n%2 == 0 @@ -202,9 +183,9 @@ A working example follows. The server code:: The client code for the preceding server:: - import xmlrpclib + import xmlrpc.client - proxy = xmlrpclib.ServerProxy("http://localhost:8000/") + proxy = xmlrpc.client.ServerProxy("http://localhost:8000/") print("3 is even: %s" % str(proxy.is_even(3))) print("100 is even: %s" % str(proxy.is_even(100))) @@ -235,12 +216,12 @@ and :meth:`__repr__` methods. A working example follows. The server code:: import datetime - from SimpleXMLRPCServer import SimpleXMLRPCServer - import xmlrpclib + from xmlrpc.server import SimpleXMLRPCServer + import xmlrpc.client def today(): today = datetime.datetime.today() - return xmlrpclib.DateTime(today) + return xmlrpc.client.DateTime(today) server = SimpleXMLRPCServer(("localhost", 8000)) print("Listening on port 8000...") @@ -249,10 +230,10 @@ A working example follows. The server code:: The client code for the preceding server:: - import xmlrpclib + import xmlrpc.client import datetime - proxy = xmlrpclib.ServerProxy("http://localhost:8000/") + proxy = xmlrpc.client.ServerProxy("http://localhost:8000/") today = proxy.today() # convert the ISO8601 string to a datetime object @@ -298,12 +279,12 @@ It also supports certain of Python's built-in operators through a Example usage of the binary objects. We're going to transfer an image over XMLRPC:: - from SimpleXMLRPCServer import SimpleXMLRPCServer - import xmlrpclib + from xmlrpc.server import SimpleXMLRPCServer + import xmlrpc.client def python_logo(): handle = open("python_logo.jpg") - return xmlrpclib.Binary(handle.read()) + return xmlrpc.client.Binary(handle.read()) handle.close() server = SimpleXMLRPCServer(("localhost", 8000)) @@ -314,9 +295,9 @@ XMLRPC:: The client gets the image and saves it to a file:: - import xmlrpclib + import xmlrpc.client - proxy = xmlrpclib.ServerProxy("http://localhost:8000/") + proxy = xmlrpc.client.ServerProxy("http://localhost:8000/") handle = open("fetched_python_logo.jpg", "w") handle.write(proxy.python_logo().data) handle.close() @@ -342,7 +323,7 @@ objects have the following members: In the following example we're going to intentionally cause a :exc:`Fault` by returning a complex type object. The server code:: - from SimpleXMLRPCServer import SimpleXMLRPCServer + from xmlrpc.server import SimpleXMLRPCServer # A marshalling error is going to occur because we're returning a # complex number @@ -357,12 +338,12 @@ returning a complex type object. The server code:: The client code for the preceding server:: - import xmlrpclib + import xmlrpc.client - proxy = xmlrpclib.ServerProxy("http://localhost:8000/") + proxy = xmlrpc.client.ServerProxy("http://localhost:8000/") try: proxy.add(2, 5) - except xmlrpclib.Fault, err: + except xmlrpc.client.Fault, err: print("A fault occured") print("Fault code: %d" % err.faultCode) print("Fault string: %s" % err.faultString) @@ -402,14 +383,14 @@ does not exist). It has the following members: In the following example we're going to intentionally cause a :exc:`ProtocolError` by providing an invalid URI:: - import xmlrpclib + import xmlrpc.client # create a ServerProxy with an invalid URI - proxy = xmlrpclib.ServerProxy("http://invalidaddress/") + proxy = xmlrpc.client.ServerProxy("http://invalidaddress/") try: proxy.some_method() - except xmlrpclib.ProtocolError, err: + except xmlrpc.client.ProtocolError, err: print("A protocol error occured") print("URL: %s" % err.url) print("HTTP/HTTPS headers: %s" % err.headers) @@ -435,7 +416,7 @@ encapsulate multiple calls to a remote server into a single request. A usage example of this class follows. The server code :: - from SimpleXMLRPCServer import SimpleXMLRPCServer + from xmlrpc.server import SimpleXMLRPCServer def add(x,y): return x+y @@ -461,10 +442,10 @@ A usage example of this class follows. The server code :: The client code for the preceding server:: - import xmlrpclib + import xmlrpc.client - proxy = xmlrpclib.ServerProxy("http://localhost:8000/") - multicall = xmlrpclib.MultiCall(proxy) + proxy = xmlrpc.client.ServerProxy("http://localhost:8000/") + multicall = xmlrpc.client.MultiCall(proxy) multicall.add(7,3) multicall.subtract(7,3) multicall.multiply(7,3) @@ -477,13 +458,6 @@ The client code for the preceding server:: Convenience Functions --------------------- - -.. function:: boolean(value) - - Convert any Python value to one of the XML-RPC Boolean constants, ``True`` or - ``False``. - - .. function:: dumps(params[, methodname[, methodresponse[, encoding[, allow_none]]]]) Convert *params* into an XML-RPC request. or into a response if *methodresponse* @@ -513,7 +487,7 @@ Example of Client Usage :: # simple test program (from the XML-RPC specification) - from xmlrpclib import ServerProxy, Error + from xmlrpc.client import ServerProxy, Error # server = ServerProxy("http://localhost:8000") # local server server = ServerProxy("http://betty.userland.com") @@ -532,9 +506,9 @@ transport. The following example shows how: :: - import xmlrpclib, httplib + import xmlrpc.client, httplib - class ProxiedTransport(xmlrpclib.Transport): + class ProxiedTransport(xmlrpc.client.Transport): def set_proxy(self, proxy): self.proxy = proxy def make_connection(self, host): @@ -548,7 +522,7 @@ transport. The following example shows how: p = ProxiedTransport() p.set_proxy('proxy-server:8080') - server = xmlrpclib.Server('http://time.xmlrpc.com/RPC2', transport=p) + server = xmlrpc.client.Server('http://time.xmlrpc.com/RPC2', transport=p) print(server.currentTime.getCurrentTime()) diff --git a/Doc/library/simplexmlrpcserver.rst b/Doc/library/xmlrpc.server.rst index f6b64b3..1a9c757 100644 --- a/Doc/library/simplexmlrpcserver.rst +++ b/Doc/library/xmlrpc.server.rst @@ -1,15 +1,14 @@ +:mod:`xmlrpc.server` --- Basic XML-RPC servers +============================================== -:mod:`SimpleXMLRPCServer` --- Basic XML-RPC server -================================================== - -.. module:: SimpleXMLRPCServer - :synopsis: Basic XML-RPC server implementation. +.. module:: xmlrpc.server + :synopsis: Basic XML-RPC server implementations. .. moduleauthor:: Brian Quinlan <brianq@activestate.com> .. sectionauthor:: Fred L. Drake, Jr. <fdrake@acm.org> -The :mod:`SimpleXMLRPCServer` module provides a basic server framework for -XML-RPC servers written in Python. Servers can either be free standing, using +The :mod:`xmlrpc.server` module provides a basic server framework for XML-RPC +servers written in Python. Servers can either be free standing, using :class:`SimpleXMLRPCServer`, or embedded in a CGI environment, using :class:`CGIXMLRPCRequestHandler`. @@ -23,7 +22,7 @@ XML-RPC servers written in Python. Servers can either be free standing, using are passed to the :class:`socketserver.TCPServer` constructor. If *logRequests* is true (the default), requests will be logged; setting this parameter to false will turn off logging. The *allow_none* and *encoding* parameters are passed - on to :mod:`xmlrpclib` and control the XML-RPC responses that will be returned + on to :mod:`xmlrpc.client` and control the XML-RPC responses that will be returned from the server. The *bind_and_activate* parameter controls whether :meth:`server_bind` and :meth:`server_activate` are called immediately by the constructor; it defaults to true. Setting it to false allows code to manipulate @@ -33,8 +32,8 @@ XML-RPC servers written in Python. Servers can either be free standing, using .. class:: CGIXMLRPCRequestHandler([allow_none[, encoding]]) Create a new instance to handle XML-RPC requests in a CGI environment. The - *allow_none* and *encoding* parameters are passed on to :mod:`xmlrpclib` and - control the XML-RPC responses that will be returned from the server. + *allow_none* and *encoding* parameters are passed on to :mod:`xmlrpc.client` + and control the XML-RPC responses that will be returned from the server. .. class:: SimpleXMLRPCRequestHandler() @@ -115,8 +114,8 @@ SimpleXMLRPCServer Example ^^^^^^^^^^^^^^^^^^^^^^^^^^ Server code:: - from SimpleXMLRPCServer import SimpleXMLRPCServer - from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler + from xmlrpc.server import SimpleXMLRPCServer + from xmlrpc.server import SimpleXMLRPCRequestHandler # Restrict to a particular path. class RequestHandler(SimpleXMLRPCRequestHandler): @@ -150,9 +149,9 @@ Server code:: The following client code will call the methods made available by the preceding server:: - import xmlrpclib + import xmlrpc.client - s = xmlrpclib.ServerProxy('http://localhost:8000') + s = xmlrpc.client.ServerProxy('http://localhost:8000') print(s.pow(2,3)) # Returns 2**3 = 8 print(s.add(2,3)) # Returns 5 print(s.mul(5,2)) # Returns 5*2 = 10 @@ -220,3 +219,89 @@ Example:: handler.register_instance(MyFuncs()) handler.handle_request() + +Documenting XMLRPC server +------------------------- + +These classes extend the above classes to serve HTML documentation in response +to HTTP GET requests. Servers can either be free standing, using +:class:`DocXMLRPCServer`, or embedded in a CGI environment, using +:class:`DocCGIXMLRPCRequestHandler`. + + +.. class:: DocXMLRPCServer(addr[, requestHandler[, logRequests[, allow_none[, encoding[, bind_and_activate]]]]]) + + Create a new server instance. All parameters have the same meaning as for + :class:`SimpleXMLRPCServer`; *requestHandler* defaults to + :class:`DocXMLRPCRequestHandler`. + + +.. class:: DocCGIXMLRPCRequestHandler() + + Create a new instance to handle XML-RPC requests in a CGI environment. + + +.. class:: DocXMLRPCRequestHandler() + + Create a new request handler instance. This request handler supports XML-RPC + POST requests, documentation GET requests, and modifies logging so that the + *logRequests* parameter to the :class:`DocXMLRPCServer` constructor parameter is + honored. + + +.. _doc-xmlrpc-servers: + +DocXMLRPCServer Objects +----------------------- + +The :class:`DocXMLRPCServer` class is derived from :class:`SimpleXMLRPCServer` +and provides a means of creating self-documenting, stand alone XML-RPC +servers. HTTP POST requests are handled as XML-RPC method calls. HTTP GET +requests are handled by generating pydoc-style HTML documentation. This allows a +server to provide its own web-based documentation. + + +.. method:: DocXMLRPCServer.set_server_title(server_title) + + Set the title used in the generated HTML documentation. This title will be used + inside the HTML "title" element. + + +.. method:: DocXMLRPCServer.set_server_name(server_name) + + Set the name used in the generated HTML documentation. This name will appear at + the top of the generated documentation inside a "h1" element. + + +.. method:: DocXMLRPCServer.set_server_documentation(server_documentation) + + Set the description used in the generated HTML documentation. This description + will appear as a paragraph, below the server name, in the documentation. + + +DocCGIXMLRPCRequestHandler +-------------------------- + +The :class:`DocCGIXMLRPCRequestHandler` class is derived from +:class:`CGIXMLRPCRequestHandler` and provides a means of creating +self-documenting, XML-RPC CGI scripts. HTTP POST requests are handled as XML-RPC +method calls. HTTP GET requests are handled by generating pydoc-style HTML +documentation. This allows a server to provide its own web-based documentation. + + +.. method:: DocCGIXMLRPCRequestHandler.set_server_title(server_title) + + Set the title used in the generated HTML documentation. This title will be used + inside the HTML "title" element. + + +.. method:: DocCGIXMLRPCRequestHandler.set_server_name(server_name) + + Set the name used in the generated HTML documentation. This name will appear at + the top of the generated documentation inside a "h1" element. + + +.. method:: DocCGIXMLRPCRequestHandler.set_server_documentation(server_documentation) + + Set the description used in the generated HTML documentation. This description + will appear as a paragraph, below the server name, in the documentation. diff --git a/Doc/license.rst b/Doc/license.rst index 05962e9..ed399dc 100644 --- a/Doc/license.rst +++ b/Doc/license.rst @@ -570,7 +570,7 @@ The :mod:`uu` module contains the following notice:: XML Remote Procedure Calls -------------------------- -The :mod:`xmlrpclib` module contains the following notice:: +The :mod:`xmlrpc.client` module contains the following notice:: The XML-RPC client interface is diff --git a/Doc/tutorial/stdlib.rst b/Doc/tutorial/stdlib.rst index 725817c..66e73a9 100644 --- a/Doc/tutorial/stdlib.rst +++ b/Doc/tutorial/stdlib.rst @@ -294,7 +294,7 @@ Batteries Included Python has a "batteries included" philosophy. This is best seen through the sophisticated and robust capabilities of its larger packages. For example: -* The :mod:`xmlrpclib` and :mod:`SimpleXMLRPCServer` modules make implementing +* The :mod:`xmlrpc.client` and :mod:`xmlrpc.server` modules make implementing remote procedure calls into an almost trivial task. Despite the modules names, no direct knowledge or handling of XML is needed. diff --git a/Lib/DocXMLRPCServer.py b/Lib/DocXMLRPCServer.py deleted file mode 100644 index 246fd74..0000000 --- a/Lib/DocXMLRPCServer.py +++ /dev/null @@ -1,283 +0,0 @@ -"""Self documenting XML-RPC Server. - -This module can be used to create XML-RPC servers that -serve pydoc-style documentation in response to HTTP -GET requests. This documentation is dynamically generated -based on the functions and methods registered with the -server. - -This module is built upon the pydoc and SimpleXMLRPCServer -modules. -""" - -import pydoc -import inspect -import re -import sys - -from SimpleXMLRPCServer import (SimpleXMLRPCServer, - SimpleXMLRPCRequestHandler, - CGIXMLRPCRequestHandler, - resolve_dotted_attribute) - -class ServerHTMLDoc(pydoc.HTMLDoc): - """Class used to generate pydoc HTML document for a server""" - - def markup(self, text, escape=None, funcs={}, classes={}, methods={}): - """Mark up some plain text, given a context of symbols to look for. - Each context dictionary maps object names to anchor names.""" - escape = escape or self.escape - results = [] - here = 0 - - # XXX Note that this regular expression does not allow for the - # hyperlinking of arbitrary strings being used as method - # names. Only methods with names consisting of word characters - # and '.'s are hyperlinked. - pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|' - r'RFC[- ]?(\d+)|' - r'PEP[- ]?(\d+)|' - r'(self\.)?((?:\w|\.)+))\b') - while 1: - match = pattern.search(text, here) - if not match: break - start, end = match.span() - results.append(escape(text[here:start])) - - all, scheme, rfc, pep, selfdot, name = match.groups() - if scheme: - url = escape(all).replace('"', '"') - results.append('<a href="%s">%s</a>' % (url, url)) - elif rfc: - url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) - results.append('<a href="%s">%s</a>' % (url, escape(all))) - elif pep: - url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) - results.append('<a href="%s">%s</a>' % (url, escape(all))) - elif text[end:end+1] == '(': - results.append(self.namelink(name, methods, funcs, classes)) - elif selfdot: - results.append('self.<strong>%s</strong>' % name) - else: - results.append(self.namelink(name, classes)) - here = end - results.append(escape(text[here:])) - return ''.join(results) - - def docroutine(self, object, name, mod=None, - funcs={}, classes={}, methods={}, cl=None): - """Produce HTML documentation for a function or method object.""" - - anchor = (cl and cl.__name__ or '') + '-' + name - note = '' - - title = '<a name="%s"><strong>%s</strong></a>' % ( - self.escape(anchor), self.escape(name)) - - if inspect.ismethod(object): - args, varargs, varkw, defaults = inspect.getargspec(object) - # exclude the argument bound to the instance, it will be - # confusing to the non-Python user - argspec = inspect.formatargspec ( - args[1:], - varargs, - varkw, - defaults, - formatvalue=self.formatvalue - ) - elif inspect.isfunction(object): - args, varargs, varkw, defaults = inspect.getargspec(object) - argspec = inspect.formatargspec( - args, varargs, varkw, defaults, formatvalue=self.formatvalue) - else: - argspec = '(...)' - - if isinstance(object, tuple): - argspec = object[0] or argspec - docstring = object[1] or "" - else: - docstring = pydoc.getdoc(object) - - decl = title + argspec + (note and self.grey( - '<font face="helvetica, arial">%s</font>' % note)) - - doc = self.markup( - docstring, self.preformat, funcs, classes, methods) - doc = doc and '<dd><tt>%s</tt></dd>' % doc - return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc) - - def docserver(self, server_name, package_documentation, methods): - """Produce HTML documentation for an XML-RPC server.""" - - fdict = {} - for key, value in methods.items(): - fdict[key] = '#-' + key - fdict[value] = fdict[key] - - server_name = self.escape(server_name) - head = '<big><big><strong>%s</strong></big></big>' % server_name - result = self.heading(head, '#ffffff', '#7799ee') - - doc = self.markup(package_documentation, self.preformat, fdict) - doc = doc and '<tt>%s</tt>' % doc - result = result + '<p>%s</p>\n' % doc - - contents = [] - method_items = sorted(methods.items()) - for key, value in method_items: - contents.append(self.docroutine(value, key, funcs=fdict)) - result = result + self.bigsection( - 'Methods', '#ffffff', '#eeaa77', ''.join(contents)) - - return result - -class XMLRPCDocGenerator: - """Generates documentation for an XML-RPC server. - - This class is designed as mix-in and should not - be constructed directly. - """ - - def __init__(self): - # setup variables used for HTML documentation - self.server_name = 'XML-RPC Server Documentation' - self.server_documentation = \ - "This server exports the following methods through the XML-RPC "\ - "protocol." - self.server_title = 'XML-RPC Server Documentation' - - def set_server_title(self, server_title): - """Set the HTML title of the generated server documentation""" - - self.server_title = server_title - - def set_server_name(self, server_name): - """Set the name of the generated HTML server documentation""" - - self.server_name = server_name - - def set_server_documentation(self, server_documentation): - """Set the documentation string for the entire server.""" - - self.server_documentation = server_documentation - - def generate_html_documentation(self): - """generate_html_documentation() => html documentation for the server - - Generates HTML documentation for the server using introspection for - installed functions and instances that do not implement the - _dispatch method. Alternatively, instances can choose to implement - the _get_method_argstring(method_name) method to provide the - argument string used in the documentation and the - _methodHelp(method_name) method to provide the help text used - in the documentation.""" - - methods = {} - - for method_name in self.system_listMethods(): - if method_name in self.funcs: - method = self.funcs[method_name] - elif self.instance is not None: - method_info = [None, None] # argspec, documentation - if hasattr(self.instance, '_get_method_argstring'): - method_info[0] = self.instance._get_method_argstring(method_name) - if hasattr(self.instance, '_methodHelp'): - method_info[1] = self.instance._methodHelp(method_name) - - method_info = tuple(method_info) - if method_info != (None, None): - method = method_info - elif not hasattr(self.instance, '_dispatch'): - try: - method = resolve_dotted_attribute( - self.instance, - method_name - ) - except AttributeError: - method = method_info - else: - method = method_info - else: - assert 0, "Could not find method in self.functions and no "\ - "instance installed" - - methods[method_name] = method - - documenter = ServerHTMLDoc() - documentation = documenter.docserver( - self.server_name, - self.server_documentation, - methods - ) - - return documenter.page(self.server_title, documentation) - -class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): - """XML-RPC and documentation request handler class. - - Handles all HTTP POST requests and attempts to decode them as - XML-RPC requests. - - Handles all HTTP GET requests and interprets them as requests - for documentation. - """ - - def do_GET(self): - """Handles the HTTP GET request. - - Interpret all HTTP GET requests as requests for server - documentation. - """ - # Check that the path is legal - if not self.is_rpc_path_valid(): - self.report_404() - return - - response = self.server.generate_html_documentation() - self.send_response(200) - self.send_header("Content-type", "text/html") - self.send_header("Content-length", str(len(response))) - self.end_headers() - self.wfile.write(response.encode()) - - # shut down the connection - self.wfile.flush() - self.connection.shutdown(1) - -class DocXMLRPCServer( SimpleXMLRPCServer, - XMLRPCDocGenerator): - """XML-RPC and HTML documentation server. - - Adds the ability to serve server documentation to the capabilities - of SimpleXMLRPCServer. - """ - - def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler, - logRequests=1, allow_none=False, encoding=None, - bind_and_activate=True): - SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, - allow_none, encoding, bind_and_activate) - XMLRPCDocGenerator.__init__(self) - -class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler, - XMLRPCDocGenerator): - """Handler for XML-RPC data and documentation requests passed through - CGI""" - - def handle_get(self): - """Handles the HTTP GET request. - - Interpret all HTTP GET requests as requests for server - documentation. - """ - - response = self.generate_html_documentation() - - print('Content-Type: text/html') - print('Content-Length: %d' % len(response)) - print() - sys.stdout.write(response) - - def __init__(self): - CGIXMLRPCRequestHandler.__init__(self) - XMLRPCDocGenerator.__init__(self) diff --git a/Lib/test/test_anydbm.py b/Lib/test/test_anydbm.py deleted file mode 100644 index aab1388..0000000 --- a/Lib/test/test_anydbm.py +++ /dev/null @@ -1,147 +0,0 @@ -#! /usr/bin/env python -"""Test script for the dbm.open function based on testdumbdbm.py""" - -import os -import unittest -import dbm -import glob -import test.support - -_fname = test.support.TESTFN - -# -# Iterates over every database module supported by dbm currently available, -# setting dbm to use each in turn, and yielding that module -# -def dbm_iterator(): - old_default = dbm._defaultmod - for module in dbm._modules.values(): - dbm._defaultmod = module - yield module - dbm._defaultmod = old_default - -# -# Clean up all scratch databases we might have created during testing -# -def delete_files(): - # we don't know the precise name the underlying database uses - # so we use glob to locate all names - for f in glob.glob(_fname + "*"): - test.support.unlink(f) - - -class AnyDBMTestCase(unittest.TestCase): - _dict = {'0': b'', - 'a': b'Python:', - 'b': b'Programming', - 'c': b'the', - 'd': b'way', - 'f': b'Guido', - 'g': b'intended', - } - - def __init__(self, *args): - unittest.TestCase.__init__(self, *args) - - def test_anydbm_creation(self): - f = dbm.open(_fname, 'c') - self.assertEqual(list(f.keys()), []) - for key in self._dict: - f[key.encode("ascii")] = self._dict[key] - self.read_helper(f) - f.close() - - def test_anydbm_modification(self): - self.init_db() - f = dbm.open(_fname, 'c') - self._dict['g'] = f[b'g'] = b"indented" - self.read_helper(f) - f.close() - - def test_anydbm_read(self): - self.init_db() - f = dbm.open(_fname, 'r') - self.read_helper(f) - f.close() - - def test_anydbm_keys(self): - self.init_db() - f = dbm.open(_fname, 'r') - keys = self.keys_helper(f) - f.close() - - def test_anydbm_access(self): - self.init_db() - f = dbm.open(_fname, 'r') - key = "a".encode("ascii") - assert(key in f) - assert(f[key] == b"Python:") - f.close() - - def read_helper(self, f): - keys = self.keys_helper(f) - for key in self._dict: - self.assertEqual(self._dict[key], f[key.encode("ascii")]) - - def init_db(self): - f = dbm.open(_fname, 'n') - for k in self._dict: - f[k.encode("ascii")] = self._dict[k] - f.close() - - def keys_helper(self, f): - keys = sorted(k.decode("ascii") for k in f.keys()) - dkeys = sorted(self._dict.keys()) - self.assertEqual(keys, dkeys) - return keys - - def tearDown(self): - delete_files() - - def setUp(self): - delete_files() - - -class WhichDBTestCase(unittest.TestCase): - # Actual test methods are added to namespace after class definition. - def __init__(self, *args): - unittest.TestCase.__init__(self, *args) - - def test_whichdb(self): - for module in dbm_iterator(): - # Check whether whichdb correctly guesses module name - # for databases opened with "module" module. - # Try with empty files first - name = module.__name__ - if name == 'dbm.dumb': - continue # whichdb can't support dbm.dumb - test.support.unlink(_fname) - f = module.open(_fname, 'c') - f.close() - self.assertEqual(name, dbm.whichdb(_fname)) - # Now add a key - f = module.open(_fname, 'w') - f[b"1"] = b"1" - # and test that we can find it - self.assertTrue(b"1" in f) - # and read it - self.assertTrue(f[b"1"] == b"1") - f.close() - self.assertEqual(name, dbm.whichdb(_fname)) - - def tearDown(self): - delete_files() - - def setUp(self): - delete_files() - - -def test_main(): - try: - for module in dbm_iterator(): - test.support.run_unittest(AnyDBMTestCase, WhichDBTestCase) - finally: - delete_files() - -if __name__ == "__main__": - test_main() diff --git a/Lib/test/test_docxmlrpc.py b/Lib/test/test_docxmlrpc.py index 2af2071..9cb9ffb 100644 --- a/Lib/test/test_docxmlrpc.py +++ b/Lib/test/test_docxmlrpc.py @@ -1,10 +1,9 @@ -from DocXMLRPCServer import DocXMLRPCServer +from xmlrpc.server import DocXMLRPCServer import httplib from test import support import threading import time import unittest -import xmlrpclib PORT = None diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index cad2b9d..25a9c9d 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -3,8 +3,8 @@ import datetime import sys import time import unittest -import xmlrpclib -import SimpleXMLRPCServer +import xmlrpc.client as xmlrpclib +import xmlrpc.server import threading import mimetools import httplib @@ -160,9 +160,9 @@ class FaultTestCase(unittest.TestCase): # this will raise AttirebuteError because code don't want us to use # private methods self.assertRaises(AttributeError, - SimpleXMLRPCServer.resolve_dotted_attribute, str, '__add') + xmlrpc.server.resolve_dotted_attribute, str, '__add') - self.assert_(SimpleXMLRPCServer.resolve_dotted_attribute(str, 'title')) + self.assert_(xmlrpc.server.resolve_dotted_attribute(str, 'title')) class DateTimeTestCase(unittest.TestCase): def test_default(self): @@ -249,7 +249,7 @@ def http_server(evt, numrequests): '''This is my function''' return True - class MyXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer): + class MyXMLRPCServer(xmlrpc.server.SimpleXMLRPCServer): def get_request(self): # Ensure the socket is always non-blocking. On Linux, socket # attributes are not inherited like they are on *BSD and Windows. @@ -306,7 +306,7 @@ def is_unavailable_exception(e): class SimpleServerTestCase(unittest.TestCase): def setUp(self): # enable traceback reporting - SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = True + xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = True self.evt = threading.Event() # start server thread to handle requests @@ -326,7 +326,7 @@ class SimpleServerTestCase(unittest.TestCase): raise RuntimeError("timeout reached, test has failed") # disable traceback reporting - SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = False + xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = False def test_simple1(self): try: @@ -443,9 +443,9 @@ class SimpleServerTestCase(unittest.TestCase): def test_dotted_attribute(self): # Raises an AttributeError because private methods are not allowed. self.assertRaises(AttributeError, - SimpleXMLRPCServer.resolve_dotted_attribute, str, '__add') + xmlrpc.server.resolve_dotted_attribute, str, '__add') - self.assert_(SimpleXMLRPCServer.resolve_dotted_attribute(str, 'title')) + self.assert_(xmlrpc.server.resolve_dotted_attribute(str, 'title')) # Get the test to run faster by sending a request with test_simple1. # This avoids waiting for the socket timeout. self.test_simple1() @@ -475,17 +475,17 @@ class FailingServerTestCase(unittest.TestCase): # wait on the server thread to terminate self.evt.wait() # reset flag - SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = False + xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = False # reset message class - SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = mimetools.Message + xmlrpc.server.SimpleXMLRPCRequestHandler.MessageClass = mimetools.Message def test_basic(self): # check that flag is false by default - flagval = SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header + flagval = xmlrpc.server.SimpleXMLRPCServer._send_traceback_header self.assertEqual(flagval, False) # enable traceback reporting - SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = True + xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = True # test a call that shouldn't fail just as a smoke test try: @@ -499,7 +499,7 @@ class FailingServerTestCase(unittest.TestCase): def test_fail_no_info(self): # use the broken message class - SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass + xmlrpc.server.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass try: p = xmlrpclib.ServerProxy('http://localhost:%d' % PORT) @@ -515,11 +515,11 @@ class FailingServerTestCase(unittest.TestCase): def test_fail_with_info(self): # use the broken message class - SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass + xmlrpc.server.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass # Check that errors in the server send back exception/traceback # info when flag is set - SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = True + xmlrpc.server.SimpleXMLRPCServer._send_traceback_header = True try: p = xmlrpclib.ServerProxy('http://localhost:%d' % PORT) @@ -536,7 +536,7 @@ class FailingServerTestCase(unittest.TestCase): class CGIHandlerTestCase(unittest.TestCase): def setUp(self): - self.cgi = SimpleXMLRPCServer.CGIXMLRPCRequestHandler() + self.cgi = xmlrpc.server.CGIXMLRPCRequestHandler() def tearDown(self): self.cgi = None diff --git a/Lib/test/test_xmlrpc_net.py b/Lib/test/test_xmlrpc_net.py index 1f8dd5d..2525254 100644 --- a/Lib/test/test_xmlrpc_net.py +++ b/Lib/test/test_xmlrpc_net.py @@ -6,7 +6,7 @@ import sys import unittest from test import support -import xmlrpclib +import xmlrpclib.client as xmlrpclib class CurrentTimeTest(unittest.TestCase): diff --git a/Lib/xmlrpc/__init__.py b/Lib/xmlrpc/__init__.py new file mode 100644 index 0000000..196d378 --- /dev/null +++ b/Lib/xmlrpc/__init__.py @@ -0,0 +1 @@ +# This directory is a Python package. diff --git a/Lib/xmlrpclib.py b/Lib/xmlrpc/client.py index 522df4d..138d86d 100644 --- a/Lib/xmlrpclib.py +++ b/Lib/xmlrpc/client.py @@ -108,7 +108,6 @@ Exported classes: ServerProxy Represents a logical connection to an XML-RPC server MultiCall Executor of boxcared xmlrpc requests - Boolean boolean wrapper to generate a "boolean" XML-RPC value DateTime dateTime wrapper for an ISO 8601 string or time tuple or localtime integer value to generate a "dateTime.iso8601" XML-RPC value @@ -127,7 +126,6 @@ Exported constants: Exported functions: - boolean Convert any Python value to an XML-RPC boolean getparser Create instance of the fastest available parser & attach to an unmarshalling object dumps Convert an argument tuple or a Fault instance to an XML-RPC @@ -147,11 +145,6 @@ try: except ImportError: datetime = None -try: - _bool_is_builtin = False.__class__.__name__ == "bool" -except NameError: - _bool_is_builtin = 0 - def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search): # decode non-ascii string (if possible) if encoding and is8bit(data): @@ -265,12 +258,7 @@ class Fault(Error): # Special values ## -# Wrapper for XML-RPC boolean values. Use the xmlrpclib.True and -# xmlrpclib.False constants, or the xmlrpclib.boolean() function, to -# generate boolean XML-RPC values. -# -# @param value A boolean value. Any true value is interpreted as True, -# all other values are interpreted as False. +# Backwards compatibility boolean = Boolean = bool @@ -451,26 +439,10 @@ def _binary(data): return value WRAPPERS = (DateTime, Binary) -if not _bool_is_builtin: - WRAPPERS = WRAPPERS + (Boolean,) # -------------------------------------------------------------------- # XML parsers -try: - # optional xmlrpclib accelerator - import _xmlrpclib - FastParser = _xmlrpclib.Parser - FastUnmarshaller = _xmlrpclib.Unmarshaller -except (AttributeError, ImportError): - FastParser = FastUnmarshaller = None - -try: - import _xmlrpclib - FastMarshaller = _xmlrpclib.Marshaller -except (AttributeError, ImportError): - FastMarshaller = None - # # the SGMLOP parser is about 15x faster than Python's builtin # XML parser. SGMLOP sources can be downloaded from: @@ -640,12 +612,11 @@ class Marshaller: write("</int></value>\n") #dispatch[int] = dump_int - if _bool_is_builtin: - def dump_bool(self, value, write): - write("<value><boolean>") - write(value and "1" or "0") - write("</boolean></value>\n") - dispatch[bool] = dump_bool + def dump_bool(self, value, write): + write("<value><boolean>") + write(value and "1" or "0") + write("</boolean></value>\n") + dispatch[bool] = dump_bool def dump_long(self, value, write): if value > MAXINT or value < MININT: @@ -968,6 +939,8 @@ class MultiCall: # -------------------------------------------------------------------- # convenience functions +FastMarshaller = FastParser = FastUnmarshaller = None + ## # Create a parser object, and connect it to an unmarshalling instance. # This function picks the fastest available XML parser. diff --git a/Lib/SimpleXMLRPCServer.py b/Lib/xmlrpc/server.py index a985153..9668c8c 100644 --- a/Lib/SimpleXMLRPCServer.py +++ b/Lib/xmlrpc/server.py @@ -1,4 +1,4 @@ -"""Simple XML-RPC Server. +"""XML-RPC Servers. This module can be used to create simple XML-RPC servers by creating a server and either installing functions, a @@ -8,6 +8,12 @@ class. It can also be used to handle XML-RPC requests in a CGI environment using CGIXMLRPCRequestHandler. +The Doc* classes can be used to create XML-RPC servers that +serve pydoc-style documentation in response to HTTP +GET requests. This documentation is dynamically generated +based on the functions and methods registered with the +server. + A list of possible usage patterns follows: 1. Install functions: @@ -98,12 +104,14 @@ server.handle_request() # Written by Brian Quinlan (brian@sweetapp.com). # Based on code written by Fredrik Lundh. -import xmlrpclib -from xmlrpclib import Fault +from xmlrpc.client import Fault, dumps, loads import socketserver import BaseHTTPServer import sys import os +import re +import pydoc +import inspect import traceback try: import fcntl @@ -235,7 +243,7 @@ class SimpleXMLRPCDispatcher: """ try: - params, method = xmlrpclib.loads(data) + params, method = loads(data) # generate response if dispatch_method is not None: @@ -244,16 +252,16 @@ class SimpleXMLRPCDispatcher: response = self._dispatch(method, params) # wrap response in a singleton tuple response = (response,) - response = xmlrpclib.dumps(response, methodresponse=1, - allow_none=self.allow_none, encoding=self.encoding) + response = dumps(response, methodresponse=1, + allow_none=self.allow_none, encoding=self.encoding) except Fault as fault: - response = xmlrpclib.dumps(fault, allow_none=self.allow_none, - encoding=self.encoding) + response = dumps(fault, allow_none=self.allow_none, + encoding=self.encoding) except: # report exception back to server exc_type, exc_value, exc_tb = sys.exc_info() - response = xmlrpclib.dumps( - xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)), + response = dumps( + Fault(1, "%s:%s" % (exc_type, exc_value)), encoding=self.encoding, allow_none=self.allow_none, ) @@ -585,6 +593,273 @@ class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): self.handle_xmlrpc(request_text) + +# ----------------------------------------------------------------------------- +# Self documenting XML-RPC Server. + +class ServerHTMLDoc(pydoc.HTMLDoc): + """Class used to generate pydoc HTML document for a server""" + + def markup(self, text, escape=None, funcs={}, classes={}, methods={}): + """Mark up some plain text, given a context of symbols to look for. + Each context dictionary maps object names to anchor names.""" + escape = escape or self.escape + results = [] + here = 0 + + # XXX Note that this regular expression does not allow for the + # hyperlinking of arbitrary strings being used as method + # names. Only methods with names consisting of word characters + # and '.'s are hyperlinked. + pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|' + r'RFC[- ]?(\d+)|' + r'PEP[- ]?(\d+)|' + r'(self\.)?((?:\w|\.)+))\b') + while 1: + match = pattern.search(text, here) + if not match: break + start, end = match.span() + results.append(escape(text[here:start])) + + all, scheme, rfc, pep, selfdot, name = match.groups() + if scheme: + url = escape(all).replace('"', '"') + results.append('<a href="%s">%s</a>' % (url, url)) + elif rfc: + url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) + results.append('<a href="%s">%s</a>' % (url, escape(all))) + elif pep: + url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) + results.append('<a href="%s">%s</a>' % (url, escape(all))) + elif text[end:end+1] == '(': + results.append(self.namelink(name, methods, funcs, classes)) + elif selfdot: + results.append('self.<strong>%s</strong>' % name) + else: + results.append(self.namelink(name, classes)) + here = end + results.append(escape(text[here:])) + return ''.join(results) + + def docroutine(self, object, name, mod=None, + funcs={}, classes={}, methods={}, cl=None): + """Produce HTML documentation for a function or method object.""" + + anchor = (cl and cl.__name__ or '') + '-' + name + note = '' + + title = '<a name="%s"><strong>%s</strong></a>' % ( + self.escape(anchor), self.escape(name)) + + if inspect.ismethod(object): + args, varargs, varkw, defaults = inspect.getargspec(object) + # exclude the argument bound to the instance, it will be + # confusing to the non-Python user + argspec = inspect.formatargspec ( + args[1:], + varargs, + varkw, + defaults, + formatvalue=self.formatvalue + ) + elif inspect.isfunction(object): + args, varargs, varkw, defaults = inspect.getargspec(object) + argspec = inspect.formatargspec( + args, varargs, varkw, defaults, formatvalue=self.formatvalue) + else: + argspec = '(...)' + + if isinstance(object, tuple): + argspec = object[0] or argspec + docstring = object[1] or "" + else: + docstring = pydoc.getdoc(object) + + decl = title + argspec + (note and self.grey( + '<font face="helvetica, arial">%s</font>' % note)) + + doc = self.markup( + docstring, self.preformat, funcs, classes, methods) + doc = doc and '<dd><tt>%s</tt></dd>' % doc + return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc) + + def docserver(self, server_name, package_documentation, methods): + """Produce HTML documentation for an XML-RPC server.""" + + fdict = {} + for key, value in methods.items(): + fdict[key] = '#-' + key + fdict[value] = fdict[key] + + server_name = self.escape(server_name) + head = '<big><big><strong>%s</strong></big></big>' % server_name + result = self.heading(head, '#ffffff', '#7799ee') + + doc = self.markup(package_documentation, self.preformat, fdict) + doc = doc and '<tt>%s</tt>' % doc + result = result + '<p>%s</p>\n' % doc + + contents = [] + method_items = sorted(methods.items()) + for key, value in method_items: + contents.append(self.docroutine(value, key, funcs=fdict)) + result = result + self.bigsection( + 'Methods', '#ffffff', '#eeaa77', ''.join(contents)) + + return result + +class XMLRPCDocGenerator: + """Generates documentation for an XML-RPC server. + + This class is designed as mix-in and should not + be constructed directly. + """ + + def __init__(self): + # setup variables used for HTML documentation + self.server_name = 'XML-RPC Server Documentation' + self.server_documentation = \ + "This server exports the following methods through the XML-RPC "\ + "protocol." + self.server_title = 'XML-RPC Server Documentation' + + def set_server_title(self, server_title): + """Set the HTML title of the generated server documentation""" + + self.server_title = server_title + + def set_server_name(self, server_name): + """Set the name of the generated HTML server documentation""" + + self.server_name = server_name + + def set_server_documentation(self, server_documentation): + """Set the documentation string for the entire server.""" + + self.server_documentation = server_documentation + + def generate_html_documentation(self): + """generate_html_documentation() => html documentation for the server + + Generates HTML documentation for the server using introspection for + installed functions and instances that do not implement the + _dispatch method. Alternatively, instances can choose to implement + the _get_method_argstring(method_name) method to provide the + argument string used in the documentation and the + _methodHelp(method_name) method to provide the help text used + in the documentation.""" + + methods = {} + + for method_name in self.system_listMethods(): + if method_name in self.funcs: + method = self.funcs[method_name] + elif self.instance is not None: + method_info = [None, None] # argspec, documentation + if hasattr(self.instance, '_get_method_argstring'): + method_info[0] = self.instance._get_method_argstring(method_name) + if hasattr(self.instance, '_methodHelp'): + method_info[1] = self.instance._methodHelp(method_name) + + method_info = tuple(method_info) + if method_info != (None, None): + method = method_info + elif not hasattr(self.instance, '_dispatch'): + try: + method = resolve_dotted_attribute( + self.instance, + method_name + ) + except AttributeError: + method = method_info + else: + method = method_info + else: + assert 0, "Could not find method in self.functions and no "\ + "instance installed" + + methods[method_name] = method + + documenter = ServerHTMLDoc() + documentation = documenter.docserver( + self.server_name, + self.server_documentation, + methods + ) + + return documenter.page(self.server_title, documentation) + +class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): + """XML-RPC and documentation request handler class. + + Handles all HTTP POST requests and attempts to decode them as + XML-RPC requests. + + Handles all HTTP GET requests and interprets them as requests + for documentation. + """ + + def do_GET(self): + """Handles the HTTP GET request. + + Interpret all HTTP GET requests as requests for server + documentation. + """ + # Check that the path is legal + if not self.is_rpc_path_valid(): + self.report_404() + return + + response = self.server.generate_html_documentation() + self.send_response(200) + self.send_header("Content-type", "text/html") + self.send_header("Content-length", str(len(response))) + self.end_headers() + self.wfile.write(response.encode()) + + # shut down the connection + self.wfile.flush() + self.connection.shutdown(1) + +class DocXMLRPCServer( SimpleXMLRPCServer, + XMLRPCDocGenerator): + """XML-RPC and HTML documentation server. + + Adds the ability to serve server documentation to the capabilities + of SimpleXMLRPCServer. + """ + + def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler, + logRequests=1, allow_none=False, encoding=None, + bind_and_activate=True): + SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, + allow_none, encoding, bind_and_activate) + XMLRPCDocGenerator.__init__(self) + +class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler, + XMLRPCDocGenerator): + """Handler for XML-RPC data and documentation requests passed through + CGI""" + + def handle_get(self): + """Handles the HTTP GET request. + + Interpret all HTTP GET requests as requests for server + documentation. + """ + + response = self.generate_html_documentation() + + print('Content-Type: text/html') + print('Content-Length: %d' % len(response)) + print() + sys.stdout.write(response) + + def __init__(self): + CGIXMLRPCRequestHandler.__init__(self) + XMLRPCDocGenerator.__init__(self) + + if __name__ == '__main__': print('Running XML-RPC server on port 8000') server = SimpleXMLRPCServer(("localhost", 8000)) @@ -56,6 +56,17 @@ Extension Modules Library ------- +- The ``xmlrpc`` package was created; it contains the old + ``xmlrpclib`` module as ``xmlrpc.client`` and the content of + the old ``SimpleXMLRPCServer`` and ``DocXMLRPCServer`` modules + as ``xmlrpc.server``. + +- The ``dbm`` package was created, containing the old modules + ``anydbm`` and ``whichdb`` in its ``__init__.py``, and having + ``dbm.gnu`` (was ``gdbm``), ``dbm.bsd`` (was ``dbhash``), + ``dbm.ndbm`` (was ``dbm``) and ``dbm.dumb`` (was ``dumbdbm``) + as submodules. + - The ``repr`` module has been renamed to ``reprlib``. - The ``statvfs`` module has been removed. diff --git a/Misc/cheatsheet b/Misc/cheatsheet index c959de5..ed7c98a 100644 --- a/Misc/cheatsheet +++ b/Misc/cheatsheet @@ -1952,7 +1952,8 @@ xdrlib Implements (a subset of) Sun XDR (eXternal Data xmllib A parser for XML, using the derived class as static DTD. xml.dom Classes for processing XML using the Document Object Model. xml.sax Classes for processing XML using the SAX API. -xmlrpclib Support for remote procedure calls using XML. +xmlrpc.client Support for remote procedure calls using XML. +xmlrpc.server Create XMLRPC servers. zipfile Read & write PK zipped files. |