[Zope3-checkins] SVN: Zope3/branches/tziade_xmlintrospection/src/zope/app/xmlrpcintrospection/ changed the signature implementation to stick with XML-RPC RFC

Tarek Ziadé tziade at nuxeo.com
Sun Oct 9 05:59:12 EDT 2005


Log message for revision 38993:
  changed the signature implementation to stick with XML-RPC RFC

Changed:
  U   Zope3/branches/tziade_xmlintrospection/src/zope/app/xmlrpcintrospection/README.txt
  U   Zope3/branches/tziade_xmlintrospection/src/zope/app/xmlrpcintrospection/xmlrpcintrospection.py

-=-
Modified: Zope3/branches/tziade_xmlintrospection/src/zope/app/xmlrpcintrospection/README.txt
===================================================================
--- Zope3/branches/tziade_xmlintrospection/src/zope/app/xmlrpcintrospection/README.txt	2005-10-09 09:37:49 UTC (rev 38992)
+++ Zope3/branches/tziade_xmlintrospection/src/zope/app/xmlrpcintrospection/README.txt	2005-10-09 09:59:12 UTC (rev 38993)
@@ -212,11 +212,47 @@
 
     - The list of attributes
 
-Let's add a new method that has i all:
+In RPC, the list of attributes has to be return in an array of type:
 
+[return type, param1 type, param2 type]
+
+Since in Python we cannot have a static type for the method return type,
+we introduce here a new mechanism based on a decorator, that let the xmlrpcview
+developer add his own signature.
+
+If the signature is not given, a defaut list is returned:
+
+[None, None, None...]
+
+The decorator append to the function objet two new parameters,
+to get back the signature.
+
+  >>> from zope.app.xmlrpcintrospection.xmlrpcintrospection import xmlrpccallable
   >>> class JacksonFiveRPC:
+  ...     @xmlrpccallable(str, str, str, str)
   ...     def says(self, a, b, c):
   ...         return '%s %s, %s, lalalala, you and me, lalalala' % (a, b, c)
+
+Let's try to get back the signature:
+
+  >>> JacksonFiveRPC().says.return_type
+  <type 'str'>
+  >>> JacksonFiveRPC().says.parameters_types
+  (<type 'str'>, <type 'str'>, <type 'str'>)
+
+The method is still callable as needed:
+
+  >>> JacksonFiveRPC().says('a', 'b', 'c')
+  'a b, c, lalalala, you and me, lalalala'
+
+Let's try out decorated and not decorated methods signatures:
+
+  >>> class JacksonFiveRPC:
+  ...     @xmlrpccallable(str, str, str, str)
+  ...     def says(self, a, b, c):
+  ...         return '%s %s, %s, lalalala, you and me, lalalala' % (a, b, c)
+  ...     def says_not_decorated(self, a, b, c):
+  ...         return '%s %s, %s, lalalala, you and me, lalalala' % (a, b, c)
   >>> from zope.configuration import xmlconfig
   >>> ignored = xmlconfig.string("""
   ... <configure
@@ -229,14 +265,14 @@
   ...
   ...   <xmlrpc:view
   ...       for="zope.app.folder.folder.IFolder"
-  ...       methods="says"
+  ...       methods="says says_not_decorated"
   ...       class="zope.app.xmlrpcintrospection.README.JacksonFiveRPC"
   ...       permission="zope.ManageContent"
   ...       />
   ... </configure>
   ... """)
 
-Now let's try to get the attributes for `says()`:
+Now let's try to get the signature for `says()`:
 
   >>> print http(r"""
   ... POST / HTTP/1.0
@@ -252,17 +288,54 @@
   ... </methodCall>
   ... """, handle_errors=False)
   HTTP/1.0 200 Ok
-  Content-Length: ...
-  Content-Type: text/xml...
+  Content-Length: 327
+  Content-Type: text/xml;charset=utf-8
   <BLANKLINE>
   <?xml version='1.0'?>
   <methodResponse>
   <params>
   <param>
-  <value><string>(a, b, c)</string></value>
+  <value><array><data>
+  <value><array><data>
+  <value><string>str</string></value>
+  <value><string>str</string></value>
+  <value><string>str</string></value>
+  <value><string>str</string></value>
+  </data></array></value>
+  </data></array></value>
   </param>
   </params>
   </methodResponse>
   <BLANKLINE>
 
-  
\ No newline at end of file
+Now let's try to get the signature for says_not_decorated()`:
+
+  >>> print http(r"""
+  ... POST / HTTP/1.0
+  ... Content-Type: text/xml
+  ...
+  ... <?xml version='1.0'?>
+  ... <methodCall>
+  ... <methodName>methodSignature</methodName>
+  ... <params>
+  ... <param>
+  ... <value>says_not_decorated</value>
+  ... </params>
+  ... </methodCall>
+  ... """, handle_errors=False)
+  HTTP/1.0 200 Ok
+  Content-Length: 267
+  Content-Type: text/xml;charset=utf-8
+  <BLANKLINE>
+  <?xml version='1.0'?>
+  <methodResponse>
+  <params>
+  <param>
+  <value><array><data>
+  <value><array><data>
+  <value><nil/></value><value><nil/></value><value><nil/></value><value><nil/></value></data></array></value>
+  </data></array></value>
+  </param>
+  </params>
+  </methodResponse>
+  <BLANKLINE>

Modified: Zope3/branches/tziade_xmlintrospection/src/zope/app/xmlrpcintrospection/xmlrpcintrospection.py
===================================================================
--- Zope3/branches/tziade_xmlintrospection/src/zope/app/xmlrpcintrospection/xmlrpcintrospection.py	2005-10-09 09:37:49 UTC (rev 38992)
+++ Zope3/branches/tziade_xmlintrospection/src/zope/app/xmlrpcintrospection/xmlrpcintrospection.py	2005-10-09 09:59:12 UTC (rev 38993)
@@ -17,12 +17,23 @@
 """
 __docformat__ = 'restructuredtext'
 
+import types
+import inspect
+
 from zope.interface import providedBy
 from zope.publisher.interfaces.xmlrpc import IXMLRPCRequest
 from zope.app.publisher.xmlrpc import XMLRPCView
 from zope.app.apidoc.presentation import getViews, filterViewRegistrations
-from zope.app.apidoc.utilities import getFunctionSignature
 
+
+def xmlrpccallable(return_type, *parameters_types):
+    def wrapper(func):
+        # adding info on the function object
+        func.return_type = return_type
+        func.parameters_types = parameters_types
+        return func
+    return wrapper
+
 class XMLRPCIntrospection(object):
 
     def listAllMethods(self):
@@ -63,6 +74,32 @@
             results.extend(filtered_adapters)
         return results
 
+    def _getFunctionArgumentSize(self, func):
+        args, varargs, varkw, defaults = inspect.getargspec(func)
+        num_params = len(args) - 1
+        if varargs is not None:
+            num_params += len(varargs)
+        if varkw is not None:
+            num_params += len(varkw)
+
+        return num_params
+
+    def _getFunctionSignature(self, func):
+        """Return the signature of a function or method."""
+        if not isinstance(func, (types.FunctionType, types.MethodType)):
+            raise TypeError("func must be a function or method")
+
+        # see if the function has been decorated
+        if hasattr(func, 'return_type') and hasattr(func, 'parameters_types'):
+            signature = [func.return_type] + list(func.parameters_types)
+            # we want to return the type name as string
+            # to avoid marshall problems
+            return [[element.__name__ for element in signature]]
+
+        # no decorator, let's just return Nones
+        # XXX: if defaults are given, we can render their type
+        return [[None] * (self._getFunctionArgumentSize(func) + 1)]
+
     #
     # Lookup APIS
     #
@@ -77,16 +114,20 @@
         adapter_registrations.sort()
         return adapter_registrations
 
-    def _getFunctionAttributesDoc(self, function):
-        return getFunctionSignature(function)
+    def _getXMLRPCMethodSignature(self, method_name):
+        """ The signature of a method is an array of
+            signatures (if the method returns multiple signatures)
+            and each array contains the return type then the parameters
+            types:
 
-    def _getXMLRPCMethodSignature(self, method_name):
+                [[return type, param1 type, param2 type, ...], [...], ...]
+        """
         interfaces = list(providedBy(self.context))
 
         for result in self._getRegistrationAdapters(interfaces):
             if result.name == method_name:
                 method = getattr(result.value, method_name)
-                return self._getFunctionAttributesDoc(method)
+                return self._getFunctionSignature(method)
         # XXX see RFC here, if we want to raise or no
         return None
 



More information about the Zope3-Checkins mailing list