Package: zope.app.xmlrpcintrospection
This Zope 3 package provides an XML-RPC introspection mechanism.Detailed DocumentationXMLRPC IntrospectionWhat’s introspection now ?This Zope 3 package provides an xmlrpcintrospection mechanism,
as defined here:http://xmlrpc-c.sourceforge.net/xmlrpc-howto/xmlrpc-howto-api-introspection.htmlIt registers three new xmlrpc methods:listMethods(): Lists all xmlrpc methods (ie views) registered for the
current objectmethodHelp(method_name): Returns the method documentation of the given
method.methodSignature(method_name): Returns the method documentation of the
given method.How do I use it ?Basically, if you want to add introspection into your XMLRPCView, you just
have to add a decorator for each method of the view, that specifies the return
type of the method and the argument types.The decorator is calledxmlrpccallable>>> from zope.app.xmlrpcintrospection.xmlrpcintrospection import xmlrpccallable
>>> from zope.app.publisher.xmlrpc import XMLRPCView
>>> class MySuperXMLRPCView(XMLRPCView):
...     @xmlrpccallable(str, str, str, str)
...     def myMethod(self, a, b, c):
...         """ my help """
...         return '%s %s, %s, lalalala, you and me, lalalala' % (a, b, c)myMethod()will then be introspectable. (find a full examples below, grep
for (*))How does it works ?It is based on introspection mechanisms provided by the apidoc package.*ripped form xmlrpc doctests*Let’s write a view that returns a folder listing:>>> class FolderListing:
...     def contents(self):
...         return list(self.context.keys())Now we’ll register it as a view:>>> from zope.configuration import xmlconfig
>>> ignored = xmlconfig.string("""
... <configure
...     xmlns="http://namespaces.zope.org/zope"
...     xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
...     >
...   <!-- We only need to do this include in this example,
...        Normally the include has already been done for us. -->
...   <include package="zope.app.publisher.xmlrpc" file="meta.zcml" />
...
...   <xmlrpc:view
...       for="zope.site.interfaces.IFolder"
...       methods="contents"
...       class="zope.app.xmlrpcintrospection.README.FolderListing"
...       permission="zope.ManageContent"
...       />
... </configure>
... """)Now, we’ll add some items to the root folder:>>> print http(r"""
... POST /@@contents.html HTTP/1.1
... Authorization: Basic bWdyOm1ncnB3
... Content-Length: 73
... Content-Type: application/x-www-form-urlencoded
...
... type_name=BrowserAdd__zope.site.folder.Folder&new_value=f1""")
HTTP/1.1 303 See Other
...>>> print http(r"""
... POST /@@contents.html HTTP/1.1
... Authorization: Basic bWdyOm1ncnB3
... Content-Length: 73
... Content-Type: application/x-www-form-urlencoded
...
... type_name=BrowserAdd__zope.site.folder.Folder&new_value=f2""")
HTTP/1.1 303 See Other
...And call our xmlrpc method:>>> print http(r"""
... POST / HTTP/1.0
... Authorization: Basic bWdyOm1ncnB3
... Content-Length: 102
... Content-Type: text/xml
...
... <?xml version='1.0'?>
... <methodCall>
... <methodName>contents</methodName>
... <params>
... </params>
... </methodCall>
... """)
HTTP/1.0 200 OK
...
<?xml version='1.0'?>
<methodResponse>
<params>
<param>
<value><array><data>
<value><string>f1</string></value>
<value><string>f2</string></value>
</data></array></value>
</param>
</params>
</methodResponse>
<BLANKLINE>*end of ripped form xmlrpc doctests*Now we want to provide to that view introspection.
Let’s add three new xmlrcp methods, that published
the introspection api.>>> ignored = xmlconfig.string("""
... <configure
...     xmlns="http://namespaces.zope.org/zope"
...     xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
...     >
...   <!-- We only need to do this include in this example,
...        Normally the include has already been done for us. -->
...   <include package="zope.app.publisher.xmlrpc" file="meta.zcml" />
...   <xmlrpc:view
...     for="zope.interface.Interface"
...     methods="listMethods  methodHelp methodSignature"
...     class="zope.app.xmlrpcintrospection.xmlrpcintrospection.XMLRPCIntrospection"
...     permission="zope.Public"
...     />
... </configure>
... """)They are linked to XMLRPCIntrospection class, that actuallyknows how to lookup to all interfacesAnd call our xmlrpc method, that should list the content method:>>> print http(r"""
... POST / HTTP/1.0
... Content-Type: text/xml
...
... <?xml version='1.0'?>
... <methodCall>
... <methodName>listMethods</methodName>
... <params>
... </params>
... </methodCall>
... """, handle_errors=False)
HTTP/1.0 200 OK
...
<?xml version='1.0'?>
<methodResponse>
...
<value><string>contents</string></value>
...
</methodResponse>
<BLANKLINE>Let’s try to add another method, to se if it gets listed…>>> class FolderListing2:
...     def contents2(self):
...         return list(self.context.keys())
>>> from zope.configuration import xmlconfig
>>> ignored = xmlconfig.string("""
... <configure
...     xmlns="http://namespaces.zope.org/zope"
...     xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
...     >
...   <!-- We only need to do this include in this example,
...        Normally the include has already been done for us. -->
...   <include package="zope.app.publisher.xmlrpc" file="meta.zcml" />
...
...   <xmlrpc:view
...       for="zope.site.interfaces.IFolder"
...       methods="contents2"
...       class="zope.app.xmlrpcintrospection.README.FolderListing2"
...       permission="zope.ManageContent"
...       />
... </configure>
... """)
>>> print http(r"""
... POST / HTTP/1.0
... Content-Type: text/xml
...
... <?xml version='1.0'?>
... <methodCall>
... <methodName>listMethods</methodName>
... <params>
... </params>
... </methodCall>
... """, handle_errors=False)
HTTP/1.0 200 OK
...
<?xml version='1.0'?>
<methodResponse>
...
<value><string>contents</string></value>
<value><string>contents2</string></value>
...
</methodResponse>
<BLANKLINE>No we want to test methodHelp and methodSignature, to check that it returns,The method docThe list of attributesIn 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
...     xmlns="http://namespaces.zope.org/zope"
...     xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
...     >
...   <!-- We only need to do this include in this example,
...        Normally the include has already been done for us. -->
...   <include package="zope.app.publisher.xmlrpc" file="meta.zcml" />
...
...   <xmlrpc:view
...       for="zope.site.folder.IFolder"
...       methods="says says_not_decorated"
...       class="zope.app.xmlrpcintrospection.README.JacksonFiveRPC"
...       permission="zope.ManageContent"
...       />
... </configure>
... """)Now let’s try to get the signature forsays():>>> print http(r"""
... POST / HTTP/1.0
... Content-Type: text/xml
...
... <?xml version='1.0'?>
... <methodCall>
... <methodName>methodSignature</methodName>
... <params>
... <param>
... <value>says</value>
... </param>
... </params>
... </methodCall>
... """, handle_errors=False)
HTTP/1.0 200 OK
...
<?xml version='1.0'?>
<methodResponse>
<params>
<param>
<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>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>
... </param>
... </params>
... </methodCall>
... """, handle_errors=False)
HTTP/1.0 200 OK
...
<?xml version='1.0'?>
<methodResponse>
<params>
<param>
<value><array><data>
<value><array><data>
<value><string>undef</string></value>
<value><string>undef</string></value>
<value><string>undef</string></value>
<value><string>undef</string></value>
</data></array></value>
</data></array></value>
</param>
</params>
</methodResponse>
<BLANKLINE>Last, but not least, the method help:>>> class JacksonFiveRPCDocumented:
...     @xmlrpccallable(str, str, str, str)
...     def says(self, a, b, c):
...         """ this is the help for
...             says()
...         """
...         return '%s %s, %s, lalalala, you and me, lalalala' % (a, b, c)
...     def says_not_documented(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
...     xmlns="http://namespaces.zope.org/zope"
...     xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
...     >
...   <!-- We only need to do this include in this example,
...        Normally the include has already been done for us. -->
...   <include package="zope.app.publisher.xmlrpc" file="meta.zcml" />
...
...   <xmlrpc:view
...       for="zope.site.folder.IFolder"
...       methods="says says_not_documented"
...       class="zope.app.xmlrpcintrospection.README.JacksonFiveRPCDocumented"
...       permission="zope.ManageContent"
...       />
... </configure>
... """)
>>> print http(r"""
... POST / HTTP/1.0
... Content-Type: text/xml
...
... <?xml version='1.0'?>
... <methodCall>
... <methodName>methodHelp</methodName>
... <params>
... <param>
... <value>says</value>
... </param>
... </params>
... </methodCall>
... """, handle_errors=False)
HTTP/1.0 200 OK
...
<?xml version='1.0'?>
<methodResponse>
<params>
<param>
<value><string> this is the help for
             says()
         </string></value>
</param>
</params>
</methodResponse>
<BLANKLINE>
>>> print http(r"""
... POST / HTTP/1.0
... Content-Type: text/xml
...
... <?xml version='1.0'?>
... <methodCall>
... <methodName>methodHelp</methodName>
... <params>
... <param>
... <value>says_not_documented</value>
... </param>
... </params>
... </methodCall>
... """, handle_errors=False)
HTTP/1.0 200 OK
...
<?xml version='1.0'?>
<methodResponse>
<params>
<param>
<value><string>undef</string></value>
</param>
</params>
</methodResponse>
<BLANKLINE>CHANGES3.5.1 (2010-02-06)Fix test by including zope.loginInclude ftesting.zcml from zope.app.securitypolicy.browser.testsInclude meta.zcml from zope.securitypolicy3.5.0 (2009-02-01)Updatezope.app.folderwithzope.site.3.4.0 (2007-11-03)Initial release independent of the main Zope tree.