diff --git a/Doc/library/xmlrpc.server.rst b/Doc/library/xmlrpc.server.rst --- a/Doc/library/xmlrpc.server.rst +++ b/Doc/library/xmlrpc.server.rst @@ -61,6 +61,19 @@ The *use_builtin_types* flag was added. +.. class:: WSGIXMLRPCRequestHandler(allow_none=False, encoding=None,\ + use_builtin_types=False) + + Create a WSGI application to handle XML-RPC requests in a WSGI environment. + The *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. The *use_builtin_types* parameter is passed to the + :func:`~xmlrpc.client.loads` function and controls which types are processed + when date/times values or binary data are received; it defaults to false. + + .. versionadded:: 3.4 + + .. class:: SimpleXMLRPCRequestHandler() Create a new request handler instance. This request handler supports ``POST`` @@ -246,13 +259,103 @@ handler.handle_request() +WSGIXMLRPCRequestHandler +------------------------ +The :class:`WSGIXMLRPCRequestHandler` can be used to handle XML-RPC requests as +a WSGI application. + +.. method:: WSGIXMLRPCRequestHandler.register_function(function, name=None) + + Register a function that can respond to XML-RPC requests. If *name* is + given, it will be the method name associated with function, otherwise + *function.__name__* will be used. *name* can be either a normal or Unicode + string, and may contain characters not legal in Python identifiers, + including the period character. + + +.. method:: WSGIXMLRPCRequestHandler.register_instance(instance) + + Register an object which is used to expose method names which have not been + registered using :meth:`register_function`. If instance contains a + :meth:`_dispatch` method, it is called with the requested method name and + the parameters from the request; the return value is returned to the client + as the result. If instance does not have a :meth:`_dispatch` method, it is + searched for an attribute matching the name of the requested method; if the + requested method name contains periods, each component of the method name + is searched for individually, with the effect that a simple hierarchical + search is performed. The value found from this search is then called with + the parameters from the request, and the return value is passed back to + the client. + + +.. method:: WSGIXMLRPCRequestHandler.register_introspection_functions() + + Register the XML-RPC introspection functions ``system.listMethods``, + ``system.methodHelp`` and ``system.methodSignature``. + + +.. method:: WSGIXMLRPCRequestHandler.register_multicall_functions() + + Register the XML-RPC multicall function ``system.multicall``. + +WSGIXMLRPCRequestHandler Example +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Example:: + + from xmlrpc.server import WSGIXMLRPCRequestHandler + from wsgiref.simple_server import make_server + + # Create the WSGI application + wsgi_app = WSGIXMLRPCRequestHandler() + + wsgi_app.register_introspection_functions() + + # Register pow() function; this will use the value of + # pow.__name__ as the name, which is just 'pow'. + wsgi_app.register_function(pow) + + # Register a function under a different name + def adder_function(x,y): + return x + y + + wsgi_app.register_function(adder_function, 'add') + + # Register an instance; all the methods of the instance are + # published as XML-RPC methods (in this case, just 'mul'). + class MyFuncs: + def mul(self, x, y): + return x * y + + wsgi_app.register_instance(MyFuncs()) + + + #Create a WSGI Server + server = make_server('',8000, wsgi_app) + + # Run the server's main loop + server.serve_forever() + +The following client code will call the methods made available by the preceding +server:: + + import xmlrpc.client + + 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 + + # Print list of available methods + print(s.system.listMethods()) + 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`. +to HTTP GET requests. Servers can be free standing using +:class:`DocXMLRPCServer`, embedded in a CGI environment using +:class:`DocCGIXMLRPCRequestHandler`, or run as a WSGI application using +:class:`DocWSGIXMLRPCRequestHandler`. .. class:: DocXMLRPCServer(addr, requestHandler=DocXMLRPCRequestHandler,\ @@ -271,6 +374,9 @@ Create a new instance to handle XML-RPC requests in a CGI environment. +.. class:: DocWSGIXMLRPCRequestHandler() + + Create a documenting WSGI application to handle XML-RPC requests. .. class:: DocXMLRPCRequestHandler() @@ -287,7 +393,7 @@ 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 +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. @@ -336,3 +442,32 @@ Set the description used in the generated HTML documentation. This description will appear as a paragraph, below the server name, in the documentation. + + +DocWSGIXMLRequestHandler +------------------------ + +The :class:`DocWSGIXMLRPCRequestHandler` class is derived from +:class:`WSGIXMLRPCRequestHandler` and provides a means of creating a +self-documenting, XML-RPC WSGI application. 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/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -12,6 +12,7 @@ import io import contextlib from test import support +from io import StringIO try: import gzip @@ -1046,6 +1047,84 @@ len(content)) +class WSGIHandlerTestCase(unittest.TestCase): + + class MockStartResponse(): + """ A Mock start_response implementation """ + + def __call__(self, status, response_headers, exc_info=None): + self.status = status + self.response_headers = response_headers + + def setUp(self): + self.wsgi = xmlrpc.server.WSGIXMLRPCRequestHandler() + self.test_data = ("