avoid as much as possible accessing logger through class instances, whenever that...
[sfa.git] / sfa / server / xmlrpcapi.py
index 456cd42..5189328 100644 (file)
@@ -3,7 +3,6 @@
 #
 
 import string
-import xmlrpclib
 
 # SOAP support is optional
 try:
@@ -19,6 +18,7 @@ except ImportError:
 #from sfa.util.faults import SfaNotImplemented, SfaAPIError, SfaInvalidAPIMethod, SfaFault
 from sfa.util.faults import SfaInvalidAPIMethod, SfaAPIError, SfaFault
 from sfa.util.sfalogging import logger
+from sfa.util.py23 import xmlrpc_client
 
 ####################
 # See "2.2 Characters" in the XML specification:
@@ -27,8 +27,13 @@ from sfa.util.sfalogging import logger
 # avoiding
 # [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF]
 
-invalid_xml_ascii = map(chr, range(0x0, 0x8) + [0xB, 0xC] + range(0xE, 0x1F))
-xml_escape_table = string.maketrans("".join(invalid_xml_ascii), "?" * len(invalid_xml_ascii))
+invalid_codepoints = range(0x0, 0x8) + [0xB, 0xC] + range(0xE, 0x1F)
+# broke with f24, somehow we get a unicode as an incoming string to be translated
+str_xml_escape_table = string.maketrans("".join((chr(x) for x in invalid_codepoints)),
+                                        "?" * len(invalid_codepoints))
+# loosely inspired from
+# http://stackoverflow.com/questions/1324067/how-do-i-get-str-translate-to-work-with-unicode-strings
+unicode_xml_escape_table = { invalid : u"?" for invalid in invalid_codepoints}
 
 def xmlrpclib_escape(s, replace = string.replace):
     """
@@ -43,7 +48,11 @@ def xmlrpclib_escape(s, replace = string.replace):
     s = replace(s, ">", ">",)
 
     # Replace invalid 7-bit control characters with '?'
-    return s.translate(xml_escape_table)
+    if isinstance(s, str):
+        return s.translate(str_xml_escape_table)
+    else:
+        return s.translate(unicode_xml_escape_table)
+
 
 def xmlrpclib_dump(self, value, write):
     """
@@ -71,47 +80,51 @@ def xmlrpclib_dump(self, value, write):
             if isinstance(value, Type):
                 f(*args)
                 return
-        raise TypeError, "cannot marshal %s objects" % type(value)
+        raise TypeError("cannot marshal %s objects" % type(value))
     else:
         f(*args)
 
 # You can't hide from me!
-xmlrpclib.Marshaller._Marshaller__dump = xmlrpclib_dump
+# Note: not quite  sure if this will still cause
+# the expected behaviour under python3
+xmlrpc_client.Marshaller._Marshaller__dump = xmlrpclib_dump
+
 
 class XmlrpcApi:
     """
-    The XmlrpcApi class implements a basic xmlrpc (or soap) service 
+    The XmlrpcApi class implements a basic xmlrpc (or soap) service
     """
 
     protocol = None
-  
-    def __init__ (self, encoding="utf-8", methods='sfa.methods'):
+
+    def __init__(self, encoding="utf-8", methods='sfa.methods'):
 
         self.encoding = encoding
-        self.source = None 
-        
+        self.source = None
+
         # flat list of method names
-        self.methods_module = methods_module = __import__(methods, fromlist=[methods])
+        self.methods_module = methods_module = __import__(
+            methods, fromlist=[methods])
         self.methods = methods_module.all
 
-        self.logger = logger
     def callable(self, method):
         """
         Return a new instance of the specified method.
         """
         # Look up method
         if method not in self.methods:
-            raise SfaInvalidAPIMethod, method
-        
+            raise SfaInvalidAPIMethod(method)
+
         # Get new instance of method
         try:
             classname = method.split(".")[-1]
-            module = __import__(self.methods_module.__name__ + "." + method, globals(), locals(), [classname])
+            module = __import__(self.methods_module.__name__ +
+                                "." + method, globals(), locals(), [classname])
             callablemethod = getattr(module, classname)(self)
             return getattr(module, classname)(self)
         except (ImportError, AttributeError):
-            raise SfaInvalidAPIMethod, method
+            logger.log_exc("Error importing method: %s" % method)
+            raise SfaInvalidAPIMethod(method)
 
     def call(self, source, method, *args):
         """
@@ -123,25 +136,25 @@ class XmlrpcApi:
         self.source = source
         return function(*args)
 
-    
     def handle(self, source, data, method_map):
         """
         Handle an XML-RPC or SOAP request from the specified source.
         """
         # Parse request into method name and arguments
         try:
-            interface = xmlrpclib
-            self.protocol = 'xmlrpclib'
-            (args, method) = xmlrpclib.loads(data)
-            if method_map.has_key(method):
+            interface = xmlrpc_client
+            self.protocol = 'xmlrpc'
+            (args, method) = xmlrpc_client.loads(data)
+            if method in method_map:
                 method = method_map[method]
             methodresponse = True
-            
-        except Exception, e:
+
+        except Exception as e:
             if SOAPpy is not None:
                 self.protocol = 'soap'
                 interface = SOAPpy
-                (r, header, body, attrs) = parseSOAPRPC(data, header = 1, body = 1, attrs = 1)
+                (r, header, body, attrs) = parseSOAPRPC(
+                    data, header=1, body=1, attrs=1)
                 method = r._name
                 args = r._aslist()
                 # XXX Support named arguments
@@ -150,35 +163,38 @@ class XmlrpcApi:
 
         try:
             result = self.call(source, method, *args)
-        except SfaFault, fault:
-            result = fault 
-        except Exception, fault:
-            self.logger.log_exc("XmlrpcApi.handle has caught Exception")
+        except SfaFault as fault:
+            result = fault
+            logger.log_exc("XmlrpcApi.handle has caught Exception")
+        except Exception as fault:
+            logger.log_exc("XmlrpcApi.handle has caught Exception")
             result = SfaAPIError(fault)
 
-
         # Return result
         response = self.prepare_response(result, method)
         return response
-    
+
     def prepare_response(self, result, method=""):
         """
         convert result to a valid xmlrpc or soap response
-        """   
-        if self.protocol == 'xmlrpclib':
+        """
+
+        if self.protocol == 'xmlrpc':
             if not isinstance(result, SfaFault):
                 result = (result,)
-            response = xmlrpclib.dumps(result, methodresponse = True, encoding = self.encoding, allow_none = 1)
+            response = xmlrpc_client.dumps(
+                result, methodresponse=True, encoding=self.encoding, allow_none=1)
         elif self.protocol == 'soap':
             if isinstance(result, Exception):
-                result = faultParameter(NS.ENV_T + ":Server", "Method Failed", method)
-                result._setDetail("Fault %d: %s" % (result.faultCode, result.faultString))
+                result = faultParameter(
+                    NS.ENV_T + ":Server", "Method Failed", method)
+                result._setDetail("Fault %d: %s" %
+                                  (result.faultCode, result.faultString))
             else:
-                response = buildSOAP(kw = {'%sResponse' % method: {'Result': result}}, encoding = self.encoding)
+                response = buildSOAP(
+                    kw={'%sResponse' % method: {'Result': result}}, encoding=self.encoding)
         else:
             if isinstance(result, Exception):
-                raise result 
-            
-        return response
+                raise result
 
+        return response