跳转到主要内容

使用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 上的首次发布

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源代码分发

wadllib-1.3.9.tar.gz (65.5 kB 查看哈希值)

上传时间 源代码

构建分发

wadllib-1.3.9-py3-none-any.whl (62.0 kB 查看哈希值)

上传时间 Python 3

支持