How to install zope.app.xmlrpcintrospection
- Download and install ActivePython
- Open Command Prompt
- Type
pypm install zope.app.xmlrpcintrospection
Dependencies
- distribute
- zope.component
- zope.interface
- zope.publisher
- [test] zope.app.securitypolicy
- [test] zope.app.testing
- [test] zope.app.zcmlfiles
- [test] zope.login
- [test] zope.site
Lastest release
This Zope 3 package provides an XML-RPC introspection mechanism.
Detailed Documentation
XMLRPC Introspection
What'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.html
It registers three new xmlrpc methods:
- listMethods(): Lists all xmlrpc methods (ie views) registered for the
current object
- methodHelp(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 called xmlrpccallable
>>> 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 actually knows how to lookup to all interfaces
And 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 doc
- The list of attributes
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 ... 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 for says():
>>> 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>
CHANGES
3.5.1 (2010-02-06)
- Fix test by including zope.login
- Include ftesting.zcml from zope.app.securitypolicy.browser.tests
- Include meta.zcml from zope.securitypolicy
3.5.0 (2009-02-01)
- Update zope.app.folder with zope.site.
3.4.0 (2007-11-03)
- Initial release independent of the main Zope tree.