From 1b7458b2a1beb86526ab067059ac18a2a0b53cb2 Mon Sep 17 00:00:00 2001 From: Florent Xicluna Date: Fri, 9 Dec 2011 22:35:06 +0100 Subject: Closes #2979: add parameter 'use_builtin_types' to the SimpleXMLRPCServer. --- Doc/library/xmlrpc.server.rst | 28 ++++++++++++++++++++++++---- Lib/test/test_xmlrpc.py | 34 ++++++++++++++++++++++++++++++++++ Lib/xmlrpc/server.py | 25 +++++++++++++++---------- 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/Doc/library/xmlrpc.server.rst b/Doc/library/xmlrpc.server.rst index 67feba6..6493fd4 100644 --- a/Doc/library/xmlrpc.server.rst +++ b/Doc/library/xmlrpc.server.rst @@ -16,7 +16,9 @@ servers written in Python. Servers can either be free standing, using :class:`CGIXMLRPCRequestHandler`. -.. class:: SimpleXMLRPCServer(addr, requestHandler=SimpleXMLRPCRequestHandler, logRequests=True, allow_none=False, encoding=None, bind_and_activate=True) +.. class:: SimpleXMLRPCServer(addr, requestHandler=SimpleXMLRPCRequestHandler,\ + logRequests=True, allow_none=False, encoding=None,\ + bind_and_activate=True, use_builtin_types=False) Create a new server instance. This class provides methods for registration of functions that can be called by the XML-RPC protocol. The *requestHandler* @@ -25,18 +27,31 @@ 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:`xmlrpc.client` 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 the *allow_reuse_address* class variable before the address is bound. + 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. + .. versionchanged:: 3.3 + The *use_builtin_types* flag was added. -.. class:: CGIXMLRPCRequestHandler(allow_none=False, encoding=None) + +.. class:: CGIXMLRPCRequestHandler(allow_none=False, encoding=None,\ + use_builtin_types=False) Create a new instance to handle XML-RPC requests in a CGI 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. + + .. versionchanged:: 3.3 + The *use_builtin_types* flag was added. .. class:: SimpleXMLRPCRequestHandler() @@ -233,12 +248,17 @@ to HTTP GET requests. Servers can either be free standing, using :class:`DocCGIXMLRPCRequestHandler`. -.. class:: DocXMLRPCServer(addr, requestHandler=DocXMLRPCRequestHandler, logRequests=True, allow_none=False, encoding=None, bind_and_activate=True) +.. class:: DocXMLRPCServer(addr, requestHandler=DocXMLRPCRequestHandler,\ + logRequests=True, allow_none=False, encoding=None,\ + bind_and_activate=True, use_builtin_types=True) Create a new server instance. All parameters have the same meaning as for :class:`SimpleXMLRPCServer`; *requestHandler* defaults to :class:`DocXMLRPCRequestHandler`. + .. versionchanged:: 3.3 + The *use_builtin_types* flag was added. + .. class:: DocCGIXMLRPCRequestHandler() diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 76723fa..e5601a5 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -1023,10 +1023,44 @@ class CGIHandlerTestCase(unittest.TestCase): len(content)) +class UseBuiltinTypesTestCase(unittest.TestCase): + + def test_use_builtin_types(self): + # SimpleXMLRPCDispatcher.__init__ accepts use_builtin_types, which + # makes all dispatch of binary data as bytes instances, and all + # dispatch of datetime argument as datetime.datetime instances. + self.log = [] + expected_bytes = b"my dog has fleas" + expected_date = datetime.datetime(2008, 5, 26, 18, 25, 12) + marshaled = xmlrpclib.dumps((expected_bytes, expected_date), 'foobar') + def foobar(*args): + self.log.extend(args) + handler = xmlrpc.server.SimpleXMLRPCDispatcher( + allow_none=True, encoding=None, use_builtin_types=True) + handler.register_function(foobar) + handler._marshaled_dispatch(marshaled) + self.assertEqual(len(self.log), 2) + mybytes, mydate = self.log + self.assertEqual(self.log, [expected_bytes, expected_date]) + self.assertIs(type(mydate), datetime.datetime) + self.assertIs(type(mybytes), bytes) + + def test_cgihandler_has_use_builtin_types_flag(self): + handler = xmlrpc.server.CGIXMLRPCRequestHandler(use_builtin_types=True) + self.assertTrue(handler.use_builtin_types) + + def test_xmlrpcserver_has_use_builtin_types_flag(self): + server = xmlrpc.server.SimpleXMLRPCServer(("localhost", 0), + use_builtin_types=True) + server.server_close() + self.assertTrue(server.use_builtin_types) + + @support.reap_threads def test_main(): xmlrpc_tests = [XMLRPCTestCase, HelperTestCase, DateTimeTestCase, BinaryTestCase, FaultTestCase] + xmlrpc_tests.append(UseBuiltinTypesTestCase) xmlrpc_tests.append(SimpleServerTestCase) xmlrpc_tests.append(KeepaliveServerTestCase1) xmlrpc_tests.append(KeepaliveServerTestCase2) diff --git a/Lib/xmlrpc/server.py b/Lib/xmlrpc/server.py index 4fc8a15..bf22aa9 100644 --- a/Lib/xmlrpc/server.py +++ b/Lib/xmlrpc/server.py @@ -160,11 +160,13 @@ class SimpleXMLRPCDispatcher: can be instanced when used by the MultiPathXMLRPCServer """ - def __init__(self, allow_none=False, encoding=None): + def __init__(self, allow_none=False, encoding=None, + use_builtin_types=False): self.funcs = {} self.instance = None self.allow_none = allow_none self.encoding = encoding or 'utf-8' + self.use_builtin_types = use_builtin_types def register_instance(self, instance, allow_dotted_names=False): """Registers an instance to respond to XML-RPC requests. @@ -245,7 +247,7 @@ class SimpleXMLRPCDispatcher: """ try: - params, method = loads(data) + params, method = loads(data, use_builtin_types=self.use_builtin_types) # generate response if dispatch_method is not None: @@ -572,10 +574,11 @@ class SimpleXMLRPCServer(socketserver.TCPServer, _send_traceback_header = False def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, bind_and_activate=True): + logRequests=True, allow_none=False, encoding=None, + bind_and_activate=True, use_builtin_types=False): self.logRequests = logRequests - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) + SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate) # [Bug #1222790] If possible, set close-on-exec flag; if a @@ -595,10 +598,11 @@ class MultiPathXMLRPCServer(SimpleXMLRPCServer): Make sure that the requestHandler accepts the paths in question. """ def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, bind_and_activate=True): + logRequests=True, allow_none=False, encoding=None, + bind_and_activate=True, use_builtin_types=False): SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none, - encoding, bind_and_activate) + encoding, bind_and_activate, use_builtin_types) self.dispatchers = {} self.allow_none = allow_none self.encoding = encoding or 'utf-8' @@ -628,8 +632,8 @@ class MultiPathXMLRPCServer(SimpleXMLRPCServer): class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): """Simple handler for XML-RPC data passed through CGI.""" - def __init__(self, allow_none=False, encoding=None): - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) + def __init__(self, allow_none=False, encoding=None, use_builtin_types=False): + SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) def handle_xmlrpc(self, request_text): """Handle a single XML-RPC request""" @@ -924,9 +928,10 @@ class DocXMLRPCServer( SimpleXMLRPCServer, def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler, logRequests=True, allow_none=False, encoding=None, - bind_and_activate=True): + bind_and_activate=True, use_builtin_types=False): SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, - allow_none, encoding, bind_and_activate) + allow_none, encoding, bind_and_activate, + use_builtin_types) XMLRPCDocGenerator.__init__(self) class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler, -- cgit v0.12