changeset 781:e46109ce944e

Introduced multicall support. Refactored XmlRpcBase (moved methods, separated dispatcher). Added Python 2.3 support.
author Alexander Schremmer <alex AT alexanderweb DOT de>
date Sat, 03 Jun 2006 19:56:31 +0200
parents 3b6a22e4e2f9
children c4c66a5a2221
files MoinMoin/support/multicall.py MoinMoin/xmlrpc/__init__.py
diffstat 2 files changed, 164 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/support/multicall.py	Sat Jun 03 19:56:31 2006 +0200
@@ -0,0 +1,70 @@
+""" XMLRPC MultiCall support for Python 2.3. Copied from xmlrpclib.py of Python 2.4.3. """
+
+try:
+    from xmlrpclib import MultiCall
+except ImportError: 
+    from xmlrpclib import Fault
+    
+    class _MultiCallMethod:
+        # some lesser magic to store calls made to a MultiCall object
+        # for batch execution
+        def __init__(self, call_list, name):
+            self.__call_list = call_list
+            self.__name = name
+        def __getattr__(self, name):
+            return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
+        def __call__(self, *args):
+            self.__call_list.append((self.__name, args))
+    
+    class MultiCallIterator:
+        """Iterates over the results of a multicall. Exceptions are
+        thrown in response to xmlrpc faults."""
+    
+        def __init__(self, results):
+            self.results = results
+    
+        def __getitem__(self, i):
+            item = self.results[i]
+            if type(item) == type({}):
+                raise Fault(item['faultCode'], item['faultString'])
+            elif type(item) == type([]):
+                return item[0]
+            else:
+                raise ValueError,\
+                      "unexpected type in multicall result"
+    
+    class MultiCall:
+        """server -> a object used to boxcar method calls
+    
+        server should be a ServerProxy object.
+    
+        Methods can be added to the MultiCall using normal
+        method call syntax e.g.:
+    
+        multicall = MultiCall(server_proxy)
+        multicall.add(2,3)
+        multicall.get_address("Guido")
+    
+        To execute the multicall, call the MultiCall object e.g.:
+    
+        add_result, address = multicall()
+        """
+    
+        def __init__(self, server):
+            self.__server = server
+            self.__call_list = []
+    
+        def __repr__(self):
+            return "<MultiCall at %x>" % id(self)
+    
+        __str__ = __repr__
+    
+        def __getattr__(self, name):
+            return _MultiCallMethod(self.__call_list, name)
+    
+        def __call__(self):
+            marshalled_list = []
+            for name, args in self.__call_list:
+                marshalled_list.append({'methodName' : name, 'params' : args})
+    
+            return MultiCallIterator(self.__server.system.multicall(marshalled_list))
--- a/MoinMoin/xmlrpc/__init__.py	Sat Jun 03 16:08:13 2006 +0200
+++ b/MoinMoin/xmlrpc/__init__.py	Sat Jun 03 19:56:31 2006 +0200
@@ -107,6 +107,100 @@
             '\n'.join(traceback.format_tb(sys.exc_info()[2])),
         )
 
+    def process(self):
+        """ xmlrpc v1 and v2 dispatcher """
+        try:
+            data = self.request.read()
+            params, method = xmlrpclib.loads(data)
+    
+            if _debug:
+                sys.stderr.write('- XMLRPC ' + '-' * 70 + '\n')
+                sys.stderr.write('%s(%s)\n\n' % (method, repr(params)))
+            
+            response = self.dispatch(method, params)
+            
+        except:
+            # report exception back to server
+            response = xmlrpclib.dumps(xmlrpclib.Fault(1, self._dump_exc()))
+        else:
+            # wrap response in a singleton tuple
+            response = (response,)
+
+            # serialize it
+            response = xmlrpclib.dumps(response, methodresponse=1)
+
+        self.request.http_headers([
+            "Content-Type: text/xml;charset=utf-8",
+            "Content-Length: %d" % len(response),
+        ])
+        self.request.write(response)
+
+        if _debug:
+            sys.stderr.write('- XMLRPC ' + '-' * 70 + '\n')
+            sys.stderr.write(response + '\n\n')
+
+    def dispatch(self, method, params):
+        method = method.replace(".", "_")
+        
+        try:
+            fn = getattr(self, 'xmlrpc_' + method)
+        except AttributeError:
+            try:
+                fn = wikiutil.importPlugin(self.request.cfg, 'xmlrpc',
+                                           method, 'execute')
+            except wikiutil.PluginMissingError:
+                response = xmlrpclib.Fault(1, "No such method: %s." %
+                                           method)
+            else:
+                response = fn(self, *params)
+        else:
+            response = fn(*params)
+        
+        return response
+
+    # Common faults -----------------------------------------------------
+    
+    def notAllowedFault(self):
+        return xmlrpclib.Fault(1, "You are not allowed to read this page.")
+
+    def noSuchPageFault(self):
+        return xmlrpclib.Fault(1, "No such page was found.")        
+
+    #############################################################################
+    ### System methods
+    #############################################################################
+
+    def xmlrpc_system_multicall(self, call_list):
+        """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => [[4], ...]
+
+        Allows the caller to package multiple XML-RPC calls into a single
+        request.
+
+        See http://www.xmlrpc.com/discuss/msgReader$1208
+        
+        Copied from SimpleXMLRPCServer.py
+        """
+
+        results = []
+        for call in call_list:
+            method_name = call['methodName']
+            params = call['params']
+
+            try:
+                # XXX A marshalling error in any response will fail the entire
+                # multicall. If someone cares they should fix this.
+                results.append([self.dispatch(method_name, params)])
+            except xmlrpclib.Fault, fault:
+                results.append(
+                    {'faultCode' : fault.faultCode,
+                     'faultString' : fault.faultString}
+                    )
+            except:
+                results.append(
+                    {'faultCode' : 1,
+                     'faultString' : "%s:%s" % (sys.exc_type, sys.exc_value)}
+                    )
+        return results
 
     #############################################################################
     ### Interface implementation
@@ -397,57 +491,6 @@
                  self._outstr(results.formatContext(hit, 180, 1)))
                 for hit in results.hits]
 
-    def process(self):
-        """ xmlrpc v1 and v2 dispatcher """
-        try:
-            data = self.request.read()
-            params, method = xmlrpclib.loads(data)
-    
-            if _debug:
-                sys.stderr.write('- XMLRPC ' + '-' * 70 + '\n')
-                sys.stderr.write('%s(%s)\n\n' % (method, repr(params)))
-            
-            try:
-                fn = getattr(self, 'xmlrpc_' + method)
-            except AttributeError:
-                try:
-                    fn = wikiutil.importPlugin(self.request.cfg, 'xmlrpc',
-                                               method, 'execute')
-                except wikiutil.PluginMissingError:
-                    response = xmlrpclib.Fault(1, "No such method: %s." %
-                                               method)
-                else:
-                    response = fn(self, *params)
-            else:
-                response = fn(*params)
-        except:
-            # report exception back to server
-            response = xmlrpclib.dumps(xmlrpclib.Fault(1, self._dump_exc()))
-        else:
-            # wrap response in a singleton tuple
-            response = (response,)
-
-            # serialize it
-            response = xmlrpclib.dumps(response, methodresponse=1)
-
-        self.request.http_headers([
-            "Content-Type: text/xml;charset=utf-8",
-            "Content-Length: %d" % len(response),
-        ])
-        self.request.write(response)
-
-        if _debug:
-            sys.stderr.write('- XMLRPC ' + '-' * 70 + '\n')
-            sys.stderr.write(response + '\n\n')
-
-    # Common faults -----------------------------------------------------
-    
-    def notAllowedFault(self):
-        return xmlrpclib.Fault(1, "You are not allowed to read this page.")
-
-    def noSuchPageFault(self):
-        return xmlrpclib.Fault(1, "No such page was found.")        
-
 
 class XmlRpc1(XmlRpcBase):