Closes #2979: add parameter 'use_builtin_types' to the SimpleXMLRPCServer.

This commit is contained in:
Florent Xicluna 2011-12-09 22:35:06 +01:00
parent e3b47152a4
commit 1b7458b2a1
3 changed files with 73 additions and 14 deletions

View File

@ -16,7 +16,9 @@ servers written in Python. Servers can either be free standing, using
:class:`CGIXMLRPCRequestHandler`. :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 Create a new server instance. This class provides methods for registration of
functions that can be called by the XML-RPC protocol. The *requestHandler* 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* are passed to the :class:`socketserver.TCPServer` constructor. If *logRequests*
is true (the default), requests will be logged; setting this parameter to false 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 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 from the server. The *bind_and_activate* parameter controls whether
:meth:`server_bind` and :meth:`server_activate` are called immediately by the :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 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 *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 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` *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. 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() .. class:: SimpleXMLRPCRequestHandler()
@ -233,12 +248,17 @@ to HTTP GET requests. Servers can either be free standing, using
:class:`DocCGIXMLRPCRequestHandler`. :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 Create a new server instance. All parameters have the same meaning as for
:class:`SimpleXMLRPCServer`; *requestHandler* defaults to :class:`SimpleXMLRPCServer`; *requestHandler* defaults to
:class:`DocXMLRPCRequestHandler`. :class:`DocXMLRPCRequestHandler`.
.. versionchanged:: 3.3
The *use_builtin_types* flag was added.
.. class:: DocCGIXMLRPCRequestHandler() .. class:: DocCGIXMLRPCRequestHandler()

View File

@ -1023,10 +1023,44 @@ class CGIHandlerTestCase(unittest.TestCase):
len(content)) 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 @support.reap_threads
def test_main(): def test_main():
xmlrpc_tests = [XMLRPCTestCase, HelperTestCase, DateTimeTestCase, xmlrpc_tests = [XMLRPCTestCase, HelperTestCase, DateTimeTestCase,
BinaryTestCase, FaultTestCase] BinaryTestCase, FaultTestCase]
xmlrpc_tests.append(UseBuiltinTypesTestCase)
xmlrpc_tests.append(SimpleServerTestCase) xmlrpc_tests.append(SimpleServerTestCase)
xmlrpc_tests.append(KeepaliveServerTestCase1) xmlrpc_tests.append(KeepaliveServerTestCase1)
xmlrpc_tests.append(KeepaliveServerTestCase2) xmlrpc_tests.append(KeepaliveServerTestCase2)

View File

@ -160,11 +160,13 @@ class SimpleXMLRPCDispatcher:
can be instanced when used by the MultiPathXMLRPCServer 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.funcs = {}
self.instance = None self.instance = None
self.allow_none = allow_none self.allow_none = allow_none
self.encoding = encoding or 'utf-8' self.encoding = encoding or 'utf-8'
self.use_builtin_types = use_builtin_types
def register_instance(self, instance, allow_dotted_names=False): def register_instance(self, instance, allow_dotted_names=False):
"""Registers an instance to respond to XML-RPC requests. """Registers an instance to respond to XML-RPC requests.
@ -245,7 +247,7 @@ class SimpleXMLRPCDispatcher:
""" """
try: try:
params, method = loads(data) params, method = loads(data, use_builtin_types=self.use_builtin_types)
# generate response # generate response
if dispatch_method is not None: if dispatch_method is not None:
@ -572,10 +574,11 @@ class SimpleXMLRPCServer(socketserver.TCPServer,
_send_traceback_header = False _send_traceback_header = False
def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, 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 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) socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate)
# [Bug #1222790] If possible, set close-on-exec flag; if a # [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. Make sure that the requestHandler accepts the paths in question.
""" """
def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, 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, SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none,
encoding, bind_and_activate) encoding, bind_and_activate, use_builtin_types)
self.dispatchers = {} self.dispatchers = {}
self.allow_none = allow_none self.allow_none = allow_none
self.encoding = encoding or 'utf-8' self.encoding = encoding or 'utf-8'
@ -628,8 +632,8 @@ class MultiPathXMLRPCServer(SimpleXMLRPCServer):
class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
"""Simple handler for XML-RPC data passed through CGI.""" """Simple handler for XML-RPC data passed through CGI."""
def __init__(self, allow_none=False, encoding=None): def __init__(self, allow_none=False, encoding=None, use_builtin_types=False):
SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding) SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types)
def handle_xmlrpc(self, request_text): def handle_xmlrpc(self, request_text):
"""Handle a single XML-RPC request""" """Handle a single XML-RPC request"""
@ -924,9 +928,10 @@ class DocXMLRPCServer( SimpleXMLRPCServer,
def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler, def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler,
logRequests=True, allow_none=False, encoding=None, 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, SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests,
allow_none, encoding, bind_and_activate) allow_none, encoding, bind_and_activate,
use_builtin_types)
XMLRPCDocGenerator.__init__(self) XMLRPCDocGenerator.__init__(self)
class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler, class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler,