[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