bpo-29615: SimpleXMLRPCDispatcher no longer chains KeyError (#260)

(or any other exception) to exception(s) raised in the dispatched methods.
Patch by Petr Motejlek.
This commit is contained in:
Petr Motejlek 2017-03-01 18:21:28 +01:00 committed by Serhiy Storchaka
parent da62373b0d
commit 3c6314c08d
3 changed files with 117 additions and 20 deletions

View File

@ -343,6 +343,94 @@ class XMLRPCTestCase(unittest.TestCase):
self.assertEqual(p.method(), 5) self.assertEqual(p.method(), 5)
self.assertEqual(p.method(), 5) self.assertEqual(p.method(), 5)
class SimpleXMLRPCDispatcherTestCase(unittest.TestCase):
class DispatchExc(Exception):
"""Raised inside the dispatched functions when checking for
chained exceptions"""
def test_call_registered_func(self):
"""Calls explicitly registered function"""
# Makes sure any exception raised inside the function has no other
# exception chained to it
exp_params = 1, 2, 3
def dispatched_func(*params):
raise self.DispatchExc(params)
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
dispatcher.register_function(dispatched_func)
with self.assertRaises(self.DispatchExc) as exc_ctx:
dispatcher._dispatch('dispatched_func', exp_params)
self.assertEqual(exc_ctx.exception.args, (exp_params,))
self.assertIsNone(exc_ctx.exception.__cause__)
self.assertIsNone(exc_ctx.exception.__context__)
def test_call_instance_func(self):
"""Calls a registered instance attribute as a function"""
# Makes sure any exception raised inside the function has no other
# exception chained to it
exp_params = 1, 2, 3
class DispatchedClass:
def dispatched_func(self, *params):
raise SimpleXMLRPCDispatcherTestCase.DispatchExc(params)
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
dispatcher.register_instance(DispatchedClass())
with self.assertRaises(self.DispatchExc) as exc_ctx:
dispatcher._dispatch('dispatched_func', exp_params)
self.assertEqual(exc_ctx.exception.args, (exp_params,))
self.assertIsNone(exc_ctx.exception.__cause__)
self.assertIsNone(exc_ctx.exception.__context__)
def test_call_dispatch_func(self):
"""Calls the registered instance's `_dispatch` function"""
# Makes sure any exception raised inside the function has no other
# exception chained to it
exp_method = 'method'
exp_params = 1, 2, 3
class TestInstance:
def _dispatch(self, method, params):
raise SimpleXMLRPCDispatcherTestCase.DispatchExc(
method, params)
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
dispatcher.register_instance(TestInstance())
with self.assertRaises(self.DispatchExc) as exc_ctx:
dispatcher._dispatch(exp_method, exp_params)
self.assertEqual(exc_ctx.exception.args, (exp_method, exp_params))
self.assertIsNone(exc_ctx.exception.__cause__)
self.assertIsNone(exc_ctx.exception.__context__)
def test_registered_func_is_none(self):
"""Calls explicitly registered function which is None"""
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
dispatcher.register_function(None, name='method')
with self.assertRaises(Exception, expected_regex='method'):
dispatcher._dispatch('method', ('param',))
def test_instance_has_no_func(self):
"""Attempts to call nonexistent function on a registered instance"""
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
dispatcher.register_instance(object())
with self.assertRaises(Exception, expected_regex='method'):
dispatcher._dispatch('method', ('param',))
def test_cannot_locate_func(self):
"""Calls a function that the dispatcher cannot locate"""
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
with self.assertRaises(Exception, expected_regex='method'):
dispatcher._dispatch('method', ('param',))
class HelperTestCase(unittest.TestCase): class HelperTestCase(unittest.TestCase):
def test_escape(self): def test_escape(self):
self.assertEqual(xmlrpclib.escape("a&b"), "a&b") self.assertEqual(xmlrpclib.escape("a&b"), "a&b")
@ -1313,7 +1401,7 @@ def test_main():
KeepaliveServerTestCase1, KeepaliveServerTestCase2, KeepaliveServerTestCase1, KeepaliveServerTestCase2,
GzipServerTestCase, GzipUtilTestCase, GzipServerTestCase, GzipUtilTestCase,
MultiPathServerTestCase, ServerProxyTestCase, FailingServerTestCase, MultiPathServerTestCase, ServerProxyTestCase, FailingServerTestCase,
CGIHandlerTestCase) CGIHandlerTestCase, SimpleXMLRPCDispatcherTestCase)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -392,31 +392,36 @@ class SimpleXMLRPCDispatcher:
not be called. not be called.
""" """
func = None
try: try:
# check to see if a matching function has been registered # call the matching registered function
func = self.funcs[method] func = self.funcs[method]
except KeyError: except KeyError:
if self.instance is not None: pass
# check for a _dispatch method
if hasattr(self.instance, '_dispatch'):
return self.instance._dispatch(method, params)
else:
# call instance method directly
try:
func = resolve_dotted_attribute(
self.instance,
method,
self.allow_dotted_names
)
except AttributeError:
pass
if func is not None:
return func(*params)
else: else:
if func is not None:
return func(*params)
raise Exception('method "%s" is not supported' % method) raise Exception('method "%s" is not supported' % method)
if self.instance is not None:
if hasattr(self.instance, '_dispatch'):
# call the `_dispatch` method on the instance
return self.instance._dispatch(method, params)
# call the instance's method directly
try:
func = resolve_dotted_attribute(
self.instance,
method,
self.allow_dotted_names
)
except AttributeError:
pass
else:
if func is not None:
return func(*params)
raise Exception('method "%s" is not supported' % method)
class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler): class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler):
"""Simple XML-RPC request handler class. """Simple XML-RPC request handler class.

View File

@ -253,6 +253,10 @@ Extension Modules
Library Library
------- -------
- bpo-29615: SimpleXMLRPCDispatcher no longer chains KeyError (or any other
exception) to exception(s) raised in the dispatched methods.
Patch by Petr Motejlek.
- bpo-7769: Method register_function() of xmlrpc.server.SimpleXMLRPCDispatcher - bpo-7769: Method register_function() of xmlrpc.server.SimpleXMLRPCDispatcher
and its subclasses can now be used as a decorator. and its subclasses can now be used as a decorator.