Skip to content

Commit c6e199f

Browse files
petr-motejlekserhiy-storchaka
authored andcommitted
bpo-29615: backport to 3.5 (#479)
1 parent 171b674 commit c6e199f

File tree

3 files changed

+117
-20
lines changed

3 files changed

+117
-20
lines changed

Lib/test/test_xmlrpc.py

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,94 @@ def run_server():
295295
self.assertEqual(p.method(), 5)
296296
self.assertEqual(p.method(), 5)
297297

298+
299+
class SimpleXMLRPCDispatcherTestCase(unittest.TestCase):
300+
class DispatchExc(Exception):
301+
"""Raised inside the dispatched functions when checking for
302+
chained exceptions"""
303+
304+
def test_call_registered_func(self):
305+
"""Calls explicitly registered function"""
306+
# Makes sure any exception raised inside the function has no other
307+
# exception chained to it
308+
309+
exp_params = 1, 2, 3
310+
311+
def dispatched_func(*params):
312+
raise self.DispatchExc(params)
313+
314+
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
315+
dispatcher.register_function(dispatched_func)
316+
with self.assertRaises(self.DispatchExc) as exc_ctx:
317+
dispatcher._dispatch('dispatched_func', exp_params)
318+
self.assertEqual(exc_ctx.exception.args, (exp_params,))
319+
self.assertIsNone(exc_ctx.exception.__cause__)
320+
self.assertIsNone(exc_ctx.exception.__context__)
321+
322+
def test_call_instance_func(self):
323+
"""Calls a registered instance attribute as a function"""
324+
# Makes sure any exception raised inside the function has no other
325+
# exception chained to it
326+
327+
exp_params = 1, 2, 3
328+
329+
class DispatchedClass:
330+
def dispatched_func(self, *params):
331+
raise SimpleXMLRPCDispatcherTestCase.DispatchExc(params)
332+
333+
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
334+
dispatcher.register_instance(DispatchedClass())
335+
with self.assertRaises(self.DispatchExc) as exc_ctx:
336+
dispatcher._dispatch('dispatched_func', exp_params)
337+
self.assertEqual(exc_ctx.exception.args, (exp_params,))
338+
self.assertIsNone(exc_ctx.exception.__cause__)
339+
self.assertIsNone(exc_ctx.exception.__context__)
340+
341+
def test_call_dispatch_func(self):
342+
"""Calls the registered instance's `_dispatch` function"""
343+
# Makes sure any exception raised inside the function has no other
344+
# exception chained to it
345+
346+
exp_method = 'method'
347+
exp_params = 1, 2, 3
348+
349+
class TestInstance:
350+
def _dispatch(self, method, params):
351+
raise SimpleXMLRPCDispatcherTestCase.DispatchExc(
352+
method, params)
353+
354+
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
355+
dispatcher.register_instance(TestInstance())
356+
with self.assertRaises(self.DispatchExc) as exc_ctx:
357+
dispatcher._dispatch(exp_method, exp_params)
358+
self.assertEqual(exc_ctx.exception.args, (exp_method, exp_params))
359+
self.assertIsNone(exc_ctx.exception.__cause__)
360+
self.assertIsNone(exc_ctx.exception.__context__)
361+
362+
def test_registered_func_is_none(self):
363+
"""Calls explicitly registered function which is None"""
364+
365+
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
366+
dispatcher.register_function(None, name='method')
367+
with self.assertRaises(Exception, expected_regex='method'):
368+
dispatcher._dispatch('method', ('param',))
369+
370+
def test_instance_has_no_func(self):
371+
"""Attempts to call nonexistent function on a registered instance"""
372+
373+
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
374+
dispatcher.register_instance(object())
375+
with self.assertRaises(Exception, expected_regex='method'):
376+
dispatcher._dispatch('method', ('param',))
377+
378+
def test_cannot_locate_func(self):
379+
"""Calls a function that the dispatcher cannot locate"""
380+
381+
dispatcher = xmlrpc.server.SimpleXMLRPCDispatcher()
382+
with self.assertRaises(Exception, expected_regex='method'):
383+
dispatcher._dispatch('method', ('param',))
384+
385+
298386
class HelperTestCase(unittest.TestCase):
299387
def test_escape(self):
300388
self.assertEqual(xmlrpclib.escape("a&b"), "a&b")
@@ -1265,7 +1353,7 @@ def test_main():
12651353
KeepaliveServerTestCase1, KeepaliveServerTestCase2,
12661354
GzipServerTestCase, GzipUtilTestCase,
12671355
MultiPathServerTestCase, ServerProxyTestCase, FailingServerTestCase,
1268-
CGIHandlerTestCase)
1356+
CGIHandlerTestCase, SimpleXMLRPCDispatcherTestCase)
12691357

12701358

12711359
if __name__ == "__main__":

Lib/xmlrpc/server.py

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -386,31 +386,36 @@ def _dispatch(self, method, params):
386386
not be called.
387387
"""
388388

389-
func = None
390389
try:
391-
# check to see if a matching function has been registered
390+
# call the matching registered function
392391
func = self.funcs[method]
393392
except KeyError:
394-
if self.instance is not None:
395-
# check for a _dispatch method
396-
if hasattr(self.instance, '_dispatch'):
397-
return self.instance._dispatch(method, params)
398-
else:
399-
# call instance method directly
400-
try:
401-
func = resolve_dotted_attribute(
402-
self.instance,
403-
method,
404-
self.allow_dotted_names
405-
)
406-
except AttributeError:
407-
pass
408-
409-
if func is not None:
410-
return func(*params)
393+
pass
411394
else:
395+
if func is not None:
396+
return func(*params)
412397
raise Exception('method "%s" is not supported' % method)
413398

399+
if self.instance is not None:
400+
if hasattr(self.instance, '_dispatch'):
401+
# call the `_dispatch` method on the instance
402+
return self.instance._dispatch(method, params)
403+
404+
# call the instance's method directly
405+
try:
406+
func = resolve_dotted_attribute(
407+
self.instance,
408+
method,
409+
self.allow_dotted_names
410+
)
411+
except AttributeError:
412+
pass
413+
else:
414+
if func is not None:
415+
return func(*params)
416+
417+
raise Exception('method "%s" is not supported' % method)
418+
414419
class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler):
415420
"""Simple XML-RPC request handler class.
416421

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ Extension Modules
3535
Library
3636
-------
3737

38+
- bpo-29615: SimpleXMLRPCDispatcher no longer chains KeyError (or any other
39+
exception) to exception(s) raised in the dispatched methods.
40+
Patch by Petr Motejlek.
41+
3842
- bpo-29704: asyncio.subprocess.SubprocessStreamProtocol no longer closes before
3943
all pipes are closed.
4044

0 commit comments

Comments
 (0)