Zope3的XML-RPC方法内省支持
zope.app.xmlrpcintrospection的Python项目详细描述
这个zope 3包提供了一个xml-rpc内省机制。
详细文档
xmlrpc内省
这个zope 3包提供了一个xmlrpcintropection机制, 定义如下:
http://xmlrpc-c.sourceforge.net/xmlrpc-howto/xmlrpc-howto-api-introspection.html
它注册了三个新的xmlrpc方法:
- 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.
基本上,如果您想在xmlrpcview中添加内省,您只需 必须为视图的每个方法添加一个decorator,它指定 方法的类型和参数类型。
这个decorator称为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()将是可内省的。(以下是完整的例子,grep) 对于(*)
它基于apidoc包提供的内省机制。
*撕裂表单xmlrpc文档*
让我们编写一个返回文件夹列表的视图:
>>> class FolderListing: ... def contents(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="contents" ... class="zope.app.xmlrpcintrospection.README.FolderListing" ... permission="zope.ManageContent" ... /> ... </configure> ... """)
现在,我们将向根文件夹添加一些项目:
>>> 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 ...
并调用xmlrpc方法:
>>> 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>
*撕裂表单的结尾xmlrpc doctests*
现在我们要对这个观点进行反思。 我们添加三个新的xmlrcp方法 内省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> ... """)
- 它们链接到xmlrpcintrospection类,实际上
- 知道如何查找所有接口
并调用xmlrpc方法,它应该列出内容方法:
>>> 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>
让我们尝试添加另一个方法,如果它被列出的话…
>>> 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>
不,我们要测试methodhelp和methodsignature,以检查它是否返回,
- The method doc
- The list of attributes
在rpc中,属性列表必须以数组的形式返回:
[返回类型,参数1类型,参数2类型]
因为在python中,方法返回类型不能有静态类型, 我们在这里介绍一种基于decorator的新机制,它让xmlrpcview 开发者添加自己的签名。
如果没有签名,则返回一个默认列表:
[无,无,无…]
decorator向函数objet附加了两个新参数, 去拿回签名。
>>> 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)
让我们试着找回签名:
>>> JacksonFiveRPC().says.return_type <type 'str'> >>> JacksonFiveRPC().says.parameters_types (<type 'str'>, <type 'str'>, <type 'str'>)
该方法仍可以根据需要调用:
>>> JacksonFiveRPC().says('a', 'b', 'c') 'a b, c, lalalala, you and me, lalalala'
让我们试试修饰和非修饰方法签名(*):
>>> 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> ... """)
现在让我们尝试获取says()的签名
现在让我们尝试获取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</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>
>>> 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>
>>> 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>
变化
3.5.1(2010-02-06)
- 通过包含zope.login修复测试
- 包括来自zope.app.securitypolicy.browser.tests的ftesting.zcml
- 包含zope.securitypolicy中的meta.zcml
3.5.0(2009-02-01)
- 用zope.site更新zope.app.folder。
3.4.0(2007-11-03)
- 独立于主zope树的初始发布。