使用WADL文件作为指南来导航HTTP资源。
项目描述
wadllib
一个应用程序对象代表一个由WADL文件描述的Web服务。
>>> import os >>> import sys >>> import pkg_resources >>> from wadllib.application import Application
应用程序构造函数的第一个参数是找到WADL文件的URL。第二个参数可以是原始WADL标记。
>>> wadl_string = pkg_resources.resource_string( ... 'wadllib.tests.data', 'launchpad-wadl.xml') >>> wadl = Application("http://api.launchpad.dev/beta/", wadl_string)
或者第二个参数可以是包含标记的打开文件句柄。
>>> cleanups = [] >>> def application_for(filename, url="http://www.example.com/"): ... wadl_stream = pkg_resources.resource_stream( ... 'wadllib.tests.data', filename) ... cleanups.append(wadl_stream) ... return Application(url, wadl_stream) >>> wadl = application_for("launchpad-wadl.xml", ... "http://api.launchpad.dev/beta/")
从表示定义创建资源
尽管每个表示都是某些HTTP资源的表示,但HTTP资源不一定直接对应于WADL <resource> 或 <resource_type> 标签。有时表示是在WADL <method> 标签内定义的。
>>> find_method = personset_resource.get_method( ... query_params={'ws.op' : 'find'}) >>> find_method.id 'people-find'>>> representation_definition = ( ... find_method.response.get_representation_definition( ... 'application/json'))
这里定义的表示可能没有WADL <resource> 或 <resource_type> 标签。这就是为什么wadllib使得仅使用表示定义就可以实例化匿名资源对象。
>>> from wadllib.application import Resource >>> anonymous_resource = Resource( ... wadl, "http://foo/", representation_definition.tag)
只要我们明确传递表示定义,我们就可以将此资源绑定到表示。
>>> anonymous_resource = anonymous_resource.bind( ... get_testdata('personset'), 'application/json', ... representation_definition=representation_definition)
一旦资源绑定到表示,我们就可以获取其参数值。
>>> print(anonymous_resource.get_parameter( ... 'total_size', 'application/json').get_value()) 63
资源实例化
如果您偶然保存了对象的URL,并且知道其类型,您可以直接构建一个资源对象,而不是通过跟随链接。
>>> from wadllib.application import Resource >>> limi_person = Resource(wadl, "http://api.launchpad.dev/beta/~limi", ... "http://api.launchpad.dev/beta/#person") >>> sorted([method.id for method in limi_person.method_iter])[:3] ['person-acceptInvitationToBeMemberOf', 'person-addMember', 'person-declineInvitationToBeMemberOf']>>> bound_limi = bind_to_testdata(limi_person, 'person-limi') >>> sorted(bound_limi.parameter_names())[:3] ['admins_collection_link', 'confirmed_email_addresses_collection_link', 'date_created'] >>> languages_link = bound_limi.get_parameter("languages_collection_link") >>> print(languages_link.get_value()) http://api.launchpad.dev/beta/~limi/languages
您可以在创建资源时将其绑定到表示。
>>> limi_data = get_testdata('person-limi') >>> bound_limi = Resource( ... wadl, "http://api.launchpad.dev/beta/~limi", ... "http://api.launchpad.dev/beta/#person", limi_data, ... "application/json") >>> print(bound_limi.get_parameter( ... "languages_collection_link").get_value()) http://api.launchpad.dev/beta/~limi/languages
默认情况下,表示被视为字符串,并根据您传递给资源构造函数的媒体类型进行处理。如果您已经处理了表示,请将“representation_needs_processing”参数传递为False。
>>> from wadllib import _make_unicode >>> processed_limi_data = json.loads(_make_unicode(limi_data)) >>> bound_limi = Resource(wadl, "http://api.launchpad.dev/beta/~limi", ... "http://api.launchpad.dev/beta/#person", processed_limi_data, ... "application/json", False) >>> print(bound_limi.get_parameter( ... "languages_collection_link").get_value()) http://api.launchpad.dev/beta/~limi/languages
通常,资源的表示类型是您通过向该资源发送标准GET请求所得到的类型。如果不是这种情况,您可以在bind()或资源构造函数的“representation_definition”参数中指定表示定义,以绑定表示的实际外观。以下是一个示例。
例如,在人员资源上有一个名为bound_limi的方法,该方法通过独特的查询参数进行标识:ws.op=getMembersByStatus。
>>> method = bound_limi.get_method( ... query_params={'ws.op' : 'findPathToTeam'})
使用GET请求调用此方法,您将获得人员列表的一页。
>>> people_page_repr_definition = ( ... method.response.get_representation_definition('application/json')) >>> people_page_repr_definition.tag.attrib['href'] 'http://api.launchpad.dev/beta/#person-page'
碰巧我们有一个人列表的一页作为测试数据。
>>> people_page_repr = get_testdata('personset')
如果我们像上面那样将资源绑定到方法调用的结果,我们将无法访问我们期望的任何参数。wadllib会认为表示类型为‘person-full’,这是bound_limi的默认GET类型。
>>> bad_people_page = bound_limi.bind(people_page_repr) >>> print(bad_people_page.get_parameter('total_size')) None
由于我们实际上没有‘person-full’表示,因此我们无法获取该类型表示的参数值。
>>> bad_people_page.get_parameter('name').get_value() Traceback (most recent call last): ... KeyError: 'name'
所以这是一个死胡同。但是,如果我们将正确的表示类型传递给bind(),我们就可以访问与‘person-page’表示关联的参数。
>>> people_page = bound_limi.bind( ... people_page_repr, ... representation_definition=people_page_repr_definition) >>> people_page.get_parameter('total_size').get_value() 63
如果您调用该方法并请求除JSON之外的媒体类型,您将不会得到任何东西。
>>> print(method.response.get_representation_definition('text/html')) None
数据类型转换
日期和dateTime参数的值将自动转换为Python datetime对象。
>>> data_type_wadl = application_for('data-types-wadl.xml') >>> service_root = data_type_wadl.get_resource_by_path('')>>> representation = json.dumps( ... {'a_date': '2007-10-20', ... 'a_datetime': '2005-06-06T08:59:51.619713+00:00'}) >>> bound_root = service_root.bind(representation, 'application/json')>>> bound_root.get_parameter('a_date').get_value() datetime.datetime(2007, 10, 20, 0, 0) >>> bound_root.get_parameter('a_datetime').get_value() datetime.datetime(2005, 6, 6, 8, ...)
‘日期’字段可以包含时间戳,而‘datetime’字段可以省略。wadllib会将两者都转换为datetime对象。
>>> representation = json.dumps( ... {'a_date': '2005-06-06T08:59:51.619713+00:00', ... 'a_datetime': '2007-10-20'}) >>> bound_root = service_root.bind(representation, 'application/json')>>> bound_root.get_parameter('a_datetime').get_value() datetime.datetime(2007, 10, 20, 0, 0) >>> bound_root.get_parameter('a_date').get_value() datetime.datetime(2005, 6, 6, 8, ...)
如果日期或dateTime参数有一个null值,您将得到None。如果值是一个无法解析为datetime对象的字符串,您将得到一个ValueError。
>>> representation = json.dumps( ... {'a_date': 'foo', 'a_datetime': None}) >>> bound_root = service_root.bind(representation, 'application/json') >>> bound_root.get_parameter('a_date').get_value() Traceback (most recent call last): ... ValueError: foo >>> print(bound_root.get_parameter('a_datetime').get_value()) None
表示创建
在调用某些方法时,您必须提供表示。representation()方法帮助您构建一个,而无需了解表示是如何组成的详细信息。
>>> create_team_method.build_representation( ... display_name='Joe Bloggs', name='joebloggs') ('application/x-www-form-urlencoded', 'display_name=Joe+Bloggs&name=joebloggs&ws.op=newTeam')
build_representation的返回值是一个包含构建表示的媒体类型和字符串表示本身的2元组。结合资源的URL,这是您需要发送表示到Web服务器所需的所有内容。
>>> bound_limi.get_method('patch').build_representation(name='limi2') ('application/json', '{"name": "limi2"}')
表示可能需要某些参数的值。
>>> create_team_method.build_representation() Traceback (most recent call last): ... ValueError: No value for required parameter 'display_name'>>> bound_limi.get_method('put').build_representation(name='limi2') Traceback (most recent call last): ... ValueError: No value for required parameter 'mugshot_link'
某些表示可以安全地包含二进制数据。
>>> binary_stream = pkg_resources.resource_stream( ... 'wadllib.tests.data', 'multipart-binary-wadl.xml') >>> cleanups.append(binary_stream) >>> binary_wadl = Application( ... "http://www.example.com/", binary_stream) >>> service_root = binary_wadl.get_resource_by_path('')
定义一个辅助器,以与 zope.publisher 相同的方式处理表示。
>>> import cgi >>> import io >>> def assert_message_parts(media_type, doc, expected): ... environ = { ... 'REQUEST_METHOD': 'POST', ... 'CONTENT_TYPE': media_type, ... 'CONTENT_LENGTH': str(len(doc)), ... } ... kwargs = ( ... {'encoding': 'UTF-8'} if sys.version_info[0] >= 3 else {}) ... fs = cgi.FieldStorage( ... fp=io.BytesIO(doc), environ=environ, keep_blank_values=1, ... **kwargs) ... values = [] ... def append_values(fields): ... for field in fields: ... if field.list: ... append_values(field.list) ... else: ... values.append(field.value) ... append_values(fs.list) ... assert values == expected, ( ... 'Expected %s, got %s' % (expected, values))>>> method = service_root.get_method('post', 'multipart/form-data') >>> media_type, doc = method.build_representation( ... text_field="text", binary_field=b"\x01\x02\r\x81\r") >>> print(media_type) multipart/form-data; boundary=... >>> assert_message_parts(media_type, doc, ['text', b'\x01\x02\r\x81\r'])>>> method = service_root.get_method('post', 'multipart/form-data') >>> media_type, doc = method.build_representation( ... text_field=u"text", binary_field=b"\x01\x02\r\x81\r") >>> print(media_type) multipart/form-data; boundary=... >>> assert_message_parts(media_type, doc, ['text', b'\x01\x02\r\x81\r'])>>> method = service_root.get_method('post', 'multipart/form-data') >>> media_type, doc = method.build_representation( ... text_field="text\n", binary_field=b"\x01\x02\r\x81\n\r") >>> print(media_type) multipart/form-data; boundary=... >>> assert_message_parts( ... media_type, doc, ['text\r\n', b'\x01\x02\r\x81\n\r'])>>> method = service_root.get_method('post', 'multipart/form-data') >>> media_type, doc = method.build_representation( ... text_field=u"text\n", binary_field=b"\x01\x02\r\x81\n\r") >>> print(media_type) multipart/form-data; boundary=... >>> assert_message_parts( ... media_type, doc, ['text\r\n', b'\x01\x02\r\x81\n\r'])>>> method = service_root.get_method('post', 'multipart/form-data') >>> media_type, doc = method.build_representation( ... text_field="text\r\nmore\r\n", ... binary_field=b"\x01\x02\r\n\x81\r\x82\n") >>> print(media_type) multipart/form-data; boundary=... >>> assert_message_parts( ... media_type, doc, ['text\r\nmore\r\n', b'\x01\x02\r\n\x81\r\x82\n'])>>> method = service_root.get_method('post', 'multipart/form-data') >>> media_type, doc = method.build_representation( ... text_field=u"text\r\nmore\r\n", ... binary_field=b"\x01\x02\r\n\x81\r\x82\n") >>> print(media_type) multipart/form-data; boundary=... >>> assert_message_parts( ... media_type, doc, ['text\r\nmore\r\n', b'\x01\x02\r\n\x81\r\x82\n'])>>> method = service_root.get_method('post', 'text/unknown') >>> method.build_representation(field="value") Traceback (most recent call last): ... ValueError: Unsupported media type: 'text/unknown'
选项
一些参数的值来自预定义的选项列表。
>>> option_wadl = application_for('options-wadl.xml') >>> definitions = option_wadl.representation_definitions >>> service_root = option_wadl.get_resource_by_path('') >>> definition = definitions['service-root-json'] >>> param = definition.params(service_root)[0] >>> print(param.name) has_options >>> sorted([option.value for option in param.options]) ['Value 1', 'Value 2']
此类参数不能接受不在列表中的值。
>>> definition.validate_param_values( ... [param], {'has_options': 'Value 1'}) {'has_options': 'Value 1'}>>> definition.validate_param_values( ... [param], {'has_options': 'Invalid value'}) Traceback (most recent call last): ... ValueError: Invalid value 'Invalid value' for parameter 'has_options': valid values are: "Value 1", "Value 2"
错误条件
如果您尝试查找不存在的资源,则将返回 None。
>>> print(wadl.get_resource_by_path('nosuchresource')) None
如果您尝试查找不存在的资源类型,则将引发异常。
>>> print(wadl.get_resource_type('#nosuchtype')) Traceback (most recent call last): KeyError: 'No such XML ID: "#nosuchtype"'
如果您尝试查找的参数与任何定义的方法不匹配,则将返回 None。
>>> print(bound_limi.get_method( ... 'post', representation_params={ 'foo' : 'bar' })) None
wadllib 的新闻
1.3.9 (2024-09-23)
legacy-cgi 仅是测试依赖项。将其设置为可选依赖项。
1.3.8 (2024-09-23)
添加对 Python 3.11-3.13 的支持。
1.3.7 (2021-11-02)
添加对 Python 3.9 和 3.10 的支持。
添加基本的 pre-commit 配置。
在 Read the Docs 上发布文档。
1.3.6 (2021-09-13)
为了支持 tox,移除 buildout 支持。[bug=922605]
调整版本策略以避免在大型环境中导入 pkg_resources,这会很慢。
1.3.5 (2021-01-20)
停止支持 Python 3.2、3.3 和 3.4。
在 Python 2 中执行 multipart/form-data 编码时,再次接受 Unicode 参数值(在 1.3.3 中损坏)。
1.3.4 (2020-04-29)
宣传对 Python 3.8 的支持。
通过使用 xml.etree.ElementTree(如果不存在 xml.etree.cElementTree)来添加 Python 3.9 兼容性。[bug=1870294]
1.3.3 (2018-07-20)
停止支持 Python < 2.6。
添加 tox 测试支持。
在本地实现 MIME multipart/form-data 编码的子集,而不是使用标准库的 email 模块,该模块对二进制部分的处理不好,并且在各种方式下破坏了字节,这取决于 Python 版本。[bug=1729754]
1.3.2 (2013-02-25)
强制排序顺序以避免由于哈希随机化导致的测试失败。LP: #1132125
请确保关闭由 pkg_resources.resource_stream() 打开的流,以避免测试套件投诉。
1.3.1 (2012-03-22)
纠正 _from_string 的双重遍历导致 datetime 问题
1.3.0 (2012-01-27)
添加 Python 3 兼容性
添加在跟随链接之前检查链接的能力。
确保样本数据被打包。
1.2.0 (2011-02-03)
现在可以在跟随链接之前检查链接,以查看它是否有 WADL 描述或是否需要使用通用 HTTP 客户端检索。
现在可以使用 .parameters() 方法遍历资源的 Parameter 对象。
1.1.8 (2010-10-27)
这次修订没有代码更改,但构建系统发生了变化(再次)。这次是包含由 setup.py 使用的 version.txt 文件。
1.1.7 (2010-10-26)
这次修订没有代码更改,但构建系统再次发生了变化,以包含用于测试的样本数据。
1.1.6 (2010-10-21)
这次修订没有代码更改,但构建系统发生了变化,以包含用于测试的样本数据。
1.1.5 (2010-05-04)
修复了一个错误(Launchpad bug 274074),该错误阻止了在直接与表示定义关联的资源中查找参数值(而不是具有表示定义的资源类型)。
1.1.4 (2009-09-15)
修复了一个错误,该错误会导致 wadllib 在不提供多部分表示的所有参数时崩溃。
1.1.3 (2009-08-26)
删除不必要的构建依赖项。
将缺少的依赖项添加到 setup 文件中。
从 setup.py 中删除 sys.path 欺骗。
1.1.2 (2009-08-20)
一致地处理 simplejson 的不同版本。
1.1.1 (2009-07-14)
使 wadllib 能够了解位于 标签之下的
1.1 (2009-07-09)
使 wadllib 能够识别和生成 multipart/form-data 表示,包括包含二进制参数的表示。
1.0 (2009-03-23)
PyPI 上的首次发布
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。