]> granicus.if.org Git - python/commitdiff
bpo-29615: backport to 3.6 (#478)
authorPetr Motejlek <petr@motejlek.net>
Sun, 5 Mar 2017 16:14:06 +0000 (17:14 +0100)
committerSerhiy Storchaka <storchaka@gmail.com>
Sun, 5 Mar 2017 16:14:06 +0000 (18:14 +0200)
Lib/test/test_xmlrpc.py
Lib/xmlrpc/server.py
Misc/NEWS

index df9c79e3df0689182129888b4f91a77ebb1401a2..69de08e9c3b252ee253368d90f90551058dd96cb 100644 (file)
@@ -343,6 +343,94 @@ class XMLRPCTestCase(unittest.TestCase):
             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):
     def test_escape(self):
         self.assertEqual(xmlrpclib.escape("a&b"), "a&amp;b")
@@ -1312,7 +1400,7 @@ def test_main():
             KeepaliveServerTestCase1, KeepaliveServerTestCase2,
             GzipServerTestCase, GzipUtilTestCase,
             MultiPathServerTestCase, ServerProxyTestCase, FailingServerTestCase,
-            CGIHandlerTestCase)
+            CGIHandlerTestCase, SimpleXMLRPCDispatcherTestCase)
 
 
 if __name__ == "__main__":
index 849bfddd84816f25c3488d5a6c8f68b44d3050e8..6faa2d6f8fa3192c436f151d58bfefa20f8a7b76 100644 (file)
@@ -386,31 +386,36 @@ class SimpleXMLRPCDispatcher:
         not be called.
         """
 
-        func = None
         try:
-            # check to see if a matching function has been registered
+            # call the matching registered function
             func = self.funcs[method]
         except KeyError:
-            if self.instance is not None:
-                # 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)
+            pass
         else:
+            if func is not None:
+                return func(*params)
             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):
     """Simple XML-RPC request handler class.
 
index b342738ac4b4759c20ee1245ebd562d20c0f2093..a12229857945c830499fcf8379e2ecdd8670e03f 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -13,6 +13,10 @@ Core and Builtins
 Library
 -------
 
+- bpo-29615: SimpleXMLRPCDispatcher no longer chains KeyError (or any other
+  exception) to exception(s) raised in the dispatched methods.
+  Patch by Petr Motejlek.
+
 
 What's New in Python 3.6.1 release candidate 1
 ==============================================