wadllib-1.3.2/ 0000775 0001750 0001750 00000000000 12112756624 013423 5 ustar barry barry 0000000 0000000 wadllib-1.3.2/MANIFEST.in 0000664 0001750 0001750 00000000135 12112731513 015146 0 ustar barry barry 0000000 0000000 recursive-include src *.json *.xml *.txt
include ez_setup.py
include COPYING.txt HACKING.txt
wadllib-1.3.2/README.txt 0000664 0001750 0001750 00000001416 12112731513 015111 0 ustar barry barry 0000000 0000000 Navigate HTTP resources using WADL files as guides.
wadllib should work with Python >= 2.4.
..
Copyright (C) 2008-2009 Canonical Ltd.
This file is part of wadllib.
wadllib is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, version 3 of the License.
wadllib is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details.
You should have received a copy of the GNU Lesser General Public License
along with wadllib. If not, see .
wadllib-1.3.2/setup.py 0000775 0001750 0001750 00000005436 12112731513 015136 0 ustar barry barry 0000000 0000000 #!/usr/bin/env python
# Copyright 2008-2009 Canonical Ltd. All rights reserved.
#
# This file is part of wadllib
#
# wadllib is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# wadllib is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with wadllib. If not, see .
import sys
try:
from setuptools import setup, find_packages
except ImportError:
import ez_setup
ez_setup.use_setuptools()
from setuptools import setup, find_packages
# generic helpers primarily for the long_description
def generate(*docname_or_string):
res = []
for value in docname_or_string:
if value.endswith('.txt'):
f = open(value)
value = f.read()
f.close()
res.append(value)
if not value.endswith('\n'):
res.append('')
return '\n'.join(res)
# end generic helpers
__version__ = open("src/wadllib/version.txt").read().strip()
install_requires = [
'setuptools',
'lazr.uri',
]
if sys.version < '2.5':
# This module is in the standard library as of Python 2.5. In Python 3,
# it doesn't work when fetched separately.
install_requires.append('elementtree')
if sys.version < '2.6':
# This module is in the standard library as of Python 2.6. In Python 3,
# it doesn't work when fetched separately.
install_requires.extend('simplejson')
setup(
name='wadllib',
version=__version__,
packages=find_packages('src'),
package_dir={'':'src'},
include_package_data=True,
zip_safe=False,
maintainer='LAZR Developers',
maintainer_email='lazr-developers@lists.launchpad.net',
description=open('README.txt').readline().strip(),
long_description=generate(
'src/wadllib/README.txt',
'src/wadllib/NEWS.txt'),
license='LGPL v3',
install_requires=install_requires,
url='https://launchpad.net/wadllib',
download_url= 'https://launchpad.net/wadllib/+download',
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
],
extras_require=dict(
docs=['Sphinx',
'z3c.recipe.sphinxdoc']
),
test_suite='wadllib.tests',
)
wadllib-1.3.2/setup.cfg 0000664 0001750 0001750 00000000073 12112756624 015244 0 ustar barry barry 0000000 0000000 [egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
wadllib-1.3.2/HACKING.txt 0000664 0001750 0001750 00000002250 12112731513 015215 0 ustar barry barry 0000000 0000000 ..
This file is part of wadllib.
wadllib is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, version 3 of the License.
wadllib is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details.
You should have received a copy of the GNU Lesser General Public License
along with wadllib. If not, see .
This project uses zc.buildout for development.
============
Introduction
============
These are guidelines for hacking on the wadllib project. But first,
please see the common hacking guidelines at:
http://dev.launchpad.net/Hacking
Getting help
------------
If you find bugs in this package, you can report them here:
https://launchpad.net/wadllib
If you want to discuss this package, join the team and mailing list here:
https://launchpad.net/~lazr-developers
or send a message to:
lazr-developers@lists.launchpad.net
wadllib-1.3.2/src/ 0000775 0001750 0001750 00000000000 12112756624 014212 5 ustar barry barry 0000000 0000000 wadllib-1.3.2/src/wadllib/ 0000775 0001750 0001750 00000000000 12112756624 015630 5 ustar barry barry 0000000 0000000 wadllib-1.3.2/src/wadllib/README.txt 0000664 0001750 0001750 00000057216 12112755512 017334 0 ustar barry barry 0000000 0000000 ..
Copyright (C) 2008-2013 Canonical Ltd.
This file is part of wadllib.
wadllib is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, version 3 of the License.
wadllib is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details.
You should have received a copy of the GNU Lesser General Public License
along with wadllib. If not, see .
wadllib
*******
An Application object represents a web service described by a WADL
file.
>>> import os
>>> import sys
>>> import pkg_resources
>>> from wadllib.application import Application
The first argument to the Application constructor is the URL at which
the WADL file was found. The second argument may be raw WADL markup.
>>> wadl_string = pkg_resources.resource_string(
... 'wadllib.tests.data', 'launchpad-wadl.xml')
>>> wadl = Application("http://api.launchpad.dev/beta/", wadl_string)
Or the second argument may be an open filehandle containing the markup.
>>> 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/")
Link navigation
===============
The preferred technique for finding a resource is to start at one of
the resources defined in the WADL file, and follow links. This code
retrieves the definition of the root resource.
>>> service_root = wadl.get_resource_by_path('')
>>> service_root.url
'http://api.launchpad.dev/beta/'
>>> service_root.type_url
'#service-root'
The service root resource supports GET.
>>> get_method = service_root.get_method('get')
>>> get_method.id
'service-root-get'
>>> get_method = service_root.get_method('GET')
>>> get_method.id
'service-root-get'
If we want to invoke this method, we send a GET request to the service
root URL.
>>> get_method.name
'get'
>>> get_method.build_request_url()
'http://api.launchpad.dev/beta/'
The WADL description of a resource knows which representations are
available for that resource. In this case, the server root resource
has a a JSON representation, and it defines parameters like
'people_collection_link', a link to a list of people in Launchpad. We
should be able to use the get_parameter() method to get the WADL
definition of the 'people_collection_link' parameter and find out more
about it--for instance, is it a link to another resource?
>>> def test_raises(exc_class, method, *args, **kwargs):
... try:
... method(*args, **kwargs)
... except Exception:
... # Contortion to support Python < 2.6 and >= 3 simultaneously.
... e = sys.exc_info()[1]
... if isinstance(e, exc_class):
... print(e)
... return
... raise
... raise Exception("Expected exception %s not raised" % exc_class)
>>> from wadllib.application import NoBoundRepresentationError
>>> link_name = 'people_collection_link'
>>> test_raises(
... NoBoundRepresentationError, service_root.get_parameter, link_name)
Resource is not bound to any representation, and no media media type was specified.
Oops. The code has no way to know whether 'people_collection_link' is
a parameter of the JSON representation or some other kind of
representation. We can pass a media type to get_parameter and let it
know which representation the parameter lives in.
>>> link_parameter = service_root.get_parameter(
... link_name, 'application/json')
>>> test_raises(NoBoundRepresentationError, link_parameter.get_value)
Resource is not bound to any representation.
Oops again. The parameter is available, but it has no value, because
there's no actual data associated with the resource. The browser can
look up the description of the GET method to make an actual GET
request to the service root, and bind the resulting representation to
the WADL description of the service root.
You can't bind just any representation to a WADL resource description.
It has to be of a media type understood by the WADL description.
>>> from wadllib.application import UnsupportedMediaTypeError
>>> test_raises(
... UnsupportedMediaTypeError, service_root.bind,
... 'Some HTML', 'text/html')
This resource doesn't define a representation for media type text/html
The WADL description of the service root resource has a JSON
representation. Here it is.
>>> json_representation = service_root.get_representation_definition(
... 'application/json')
>>> json_representation.media_type
'application/json'
We already have a WADL representation of the service root resource, so
let's try binding it to that JSON representation. We use test JSON
data from a file to simulate the result of a GET request to the
service root.
>>> def get_testdata(filename):
... return pkg_resources.resource_string(
... 'wadllib.tests.data', filename + '.json')
>>> def bind_to_testdata(resource, filename):
... return resource.bind(get_testdata(filename), 'application/json')
The return value is a new Resource object that's "bound" to that JSON
test data.
>>> bound_service_root = bind_to_testdata(service_root, 'root')
>>> sorted([param.name for param in bound_service_root.parameters()])
['bugs_collection_link', 'people_collection_link']
>>> sorted(bound_service_root.parameter_names())
['bugs_collection_link', 'people_collection_link']
>>> [method.id for method in bound_service_root.method_iter]
['service-root-get']
Now the bound resource object has a JSON representation, and now
'people_collection_link' makes sense. We can follow the
'people_collection_link' to a new Resource object.
>>> link_parameter = bound_service_root.get_parameter(link_name)
>>> link_parameter.style
'plain'
>>> print(link_parameter.get_value())
http://api.launchpad.dev/beta/people
>>> personset_resource = link_parameter.linked_resource
>>> personset_resource.__class__
>>> print(personset_resource.url)
http://api.launchpad.dev/beta/people
>>> personset_resource.type_url
'http://api.launchpad.dev/beta/#people'
This new resource is a collection of people.
>>> personset_resource.id
'people'
The "collection of people" resource supports a standard GET request as
well as a special GET and an overloaded POST. The get_method() method
is used to retrieve WADL definitions of the possible HTTP requests you
might make. Here's how to get the WADL definition of the standard GET
request.
>>> get_method = personset_resource.get_method('get')
>>> get_method.id
'people-get'
The method name passed into get_method() is treated case-insensitively.
>>> personset_resource.get_method('GET').id
'people-get'
To invoke the special GET request, the client sets the 'ws.op' query
parameter to the fixed string 'findPerson'.
>>> find_method = personset_resource.get_method(
... query_params={'ws.op' : 'findPerson'})
>>> find_method.id
'people-findPerson'
Given an end-user's values for the non-fixed parameters, it's possible
to get the URL that should be used to invoke the method.
>>> print(find_method.build_request_url(text='foo'))
http://api.launchpad.dev/beta/people?text=foo&ws.op=findPerson
>>> print(find_method.build_request_url(
... {'ws.op' : 'findPerson', 'text' : 'bar'}))
http://api.launchpad.dev/beta/people?text=bar&ws.op=findPerson
An error occurs if the end-user gives an incorrect value for a fixed
parameter value, or omits a required parameter.
>>> find_method.build_request_url()
Traceback (most recent call last):
...
ValueError: No value for required parameter 'text'
>>> find_method.build_request_url(
... {'ws.op' : 'findAPerson', 'text' : 'foo'})
... # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
Traceback (most recent call last):
...
ValueError: Value 'findAPerson' for parameter 'ws.op' conflicts
with fixed value 'findPerson'
To invoke the overloaded POST request, the client sets the 'ws.op'
query variable to the fixed string 'newTeam':
>>> create_team_method = personset_resource.get_method(
... 'post', representation_params={'ws.op' : 'newTeam'})
>>> create_team_method.id
'people-newTeam'
findMethod() returns None when there's no WADL method matching the
name or the fixed parameters.
>>> print(personset_resource.get_method('nosuchmethod'))
None
>>> print(personset_resource.get_method(
... 'post', query_params={'ws_op' : 'nosuchparam'}))
None
Let's say the browser makes a GET request to the person set resource
and gets back a representation. We can bind that representation to our
description of the person set resource.
>>> bound_personset = bind_to_testdata(personset_resource, 'personset')
>>> bound_personset.get_parameter("start").get_value()
0
>>> bound_personset.get_parameter("total_size").get_value()
63
We can keep following links indefinitely, so long as we bind to a
representation to each resource as we get it, and use the
representation to find the next link.
>>> next_page_link = bound_personset.get_parameter("next_collection_link")
>>> print(next_page_link.get_value())
http://api.launchpad.dev/beta/people?ws.start=5&ws.size=5
>>> page_two = next_page_link.linked_resource
>>> bound_page_two = bind_to_testdata(page_two, 'personset-page2')
>>> print(bound_page_two.url)
http://api.launchpad.dev/beta/people?ws.start=5&ws.size=5
>>> bound_page_two.get_parameter("start").get_value()
5
>>> print(bound_page_two.get_parameter("next_collection_link").get_value())
http://api.launchpad.dev/beta/people?ws.start=10&ws.size=5
Let's say the browser makes a POST request that invokes the 'newTeam'
named operation. The response will include a number of HTTP headers,
including 'Location', which points the way to the newly created team.
>>> headers = { 'Location' : 'http://api.launchpad.dev/~newteam' }
>>> response = create_team_method.response.bind(headers)
>>> location_parameter = response.get_parameter('Location')
>>> location_parameter.get_value()
'http://api.launchpad.dev/~newteam'
>>> new_team = location_parameter.linked_resource
>>> new_team.url
'http://api.launchpad.dev/~newteam'
>>> new_team.type_url
'http://api.launchpad.dev/beta/#team'
Examining links
---------------
The 'linked_resource' property of a parameter lets you follow a link
to another object. The 'link' property of a parameter lets you examine
links before following them.
>>> try:
... import simplejson as json
... except ImportError:
... import json
>>> links_wadl = application_for('links-wadl.xml')
>>> service_root = links_wadl.get_resource_by_path('')
>>> representation = json.dumps(
... {'scalar_value': 'foo',
... 'known_link': 'http://known/',
... 'unknown_link': 'http://unknown/'})
>>> bound_root = service_root.bind(representation)
>>> print(bound_root.get_parameter("scalar_value").link)
None
>>> known_resource = bound_root.get_parameter("known_link")
>>> unknown_resource = bound_root.get_parameter("unknown_link")
>>> print(known_resource.link.can_follow)
True
>>> print(unknown_resource.link.can_follow)
False
A link whose type is unknown is a link to a resource not described by
WADL. Following this link using .linked_resource or .link.follow will
cause a wadllib error. You'll need to follow the link using a general
HTTP library or some other tool.
>>> known_resource.link.follow
>>> known_resource.linked_resource
>>> from wadllib.application import WADLError
>>> test_raises(WADLError, getattr, unknown_resource.link, 'follow')
Cannot follow a link when the target has no WADL
description. Try using a general HTTP client instead.
>>> test_raises(WADLError, getattr, unknown_resource, 'linked_resource')
Cannot follow a link when the target has no WADL
description. Try using a general HTTP client instead.
Creating a Resource from a representation definition
====================================================
Although every representation is a representation of some HTTP
resource, an HTTP resource doesn't necessarily correspond directly to
a WADL or tag. Sometimes a representation
is defined within a WADL tag.
>>> 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'))
There may be no WADL or tag for the
representation defined here. That's why wadllib makes it possible to
instantiate an anonymous Resource object using only the representation
definition.
>>> from wadllib.application import Resource
>>> anonymous_resource = Resource(
... wadl, "http://foo/", representation_definition.tag)
We can bind this resource to a representation, as long as we
explicitly pass in the representation definition.
>>> anonymous_resource = anonymous_resource.bind(
... get_testdata('personset'), 'application/json',
... representation_definition=representation_definition)
Once the resource is bound to a representation, we can get its
parameter values.
>>> print(anonymous_resource.get_parameter(
... 'total_size', 'application/json').get_value())
63
Resource instantiation
======================
If you happen to have the URL to an object lying around, and you know
its type, you can construct a Resource object directly instead of
by following links.
>>> 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
You can bind a Resource to a representation when you create it.
>>> 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
By default the representation is treated as a string and processed
according to the media type you pass into the Resource constructor. If
you've already processed the representation, pass in False for the
'representation_needs_processing' argument.
>>> 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
Most of the time, the representation of a resource is of the type
you'd get by sending a standard GET to that resource. If that's not
the case, you can specify a RepresentationDefinition as the
'representation_definition' argument to bind() or the Resource
constructor, to show what the representation really looks like. Here's
an example.
There's a method on a person resource such as bound_limi that's
identified by a distinctive query argument: ws.op=getMembersByStatus.
>>> method = bound_limi.get_method(
... query_params={'ws.op' : 'findPathToTeam'})
Invoke this method with a GET request and you'll get back a page from
a list of people.
>>> 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'
As it happens, we have a page from a list of people to use as test data.
>>> people_page_repr = get_testdata('personset')
If we bind the resource to the result of the method invocation as
happened above, we don't be able to access any of the parameters we'd
expect. wadllib will think the representation is of type
'person-full', the default GET type for bound_limi.
>>> bad_people_page = bound_limi.bind(people_page_repr)
>>> print(bad_people_page.get_parameter('total_size'))
None
Since we don't actually have a 'person-full' representation, we won't
be able to get values for the parameters of that kind of
representation.
>>> bad_people_page.get_parameter('name').get_value()
Traceback (most recent call last):
...
KeyError: 'name'
So that's a dead end. *But*, if we pass the correct representation
type into bind(), we can access the parameters associated with a
'person-page' representation.
>>> people_page = bound_limi.bind(
... people_page_repr,
... representation_definition=people_page_repr_definition)
>>> people_page.get_parameter('total_size').get_value()
63
If you invoke the method and ask for a media type other than JSON, you
won't get anything.
>>> print(method.response.get_representation_definition('text/html'))
None
Data type conversion
--------------------
The values of date and dateTime parameters are automatically converted to
Python datetime objects.
>>> 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, ...)
A 'date' field can include a timestamp, and a 'datetime' field can
omit one. wadllib will turn both into datetime objects.
>>> 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, ...)
If a date or dateTime parameter has a null value, you get None. If the
value is a string that can't be parsed to a datetime object, you get a
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 creation
=======================
You must provide a representation when invoking certain methods. The
representation() method helps you build one without knowing the
details of how a representation is put together.
>>> 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')
The return value of build_representation is a 2-tuple containing the
media type of the built representation, and the string representation
itself. Along with the resource's URL, this is all you need to send
the representation to a web server.
>>> bound_limi.get_method('patch').build_representation(name='limi2')
('application/json', '{"name": "limi2"}')
Representations may require values for certain parameters.
>>> 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'
Some representations may safely include binary data.
>>> 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('')
>>> method = service_root.get_method('post', 'multipart/form-data')
>>> media_type, doc = method.build_representation(
... text_field="text", binary_field="\x01\x02")
>>> print(media_type)
multipart/form-data; boundary=...
>>> print(doc)
MIME-Version: 1.0
Content-Type: multipart/form-data; boundary="..."
--...
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Disposition: form-data; name="text_field"
text
--...
Content-Type: application/octet-stream
MIME-Version: 1.0
Content-Disposition: form-data; name="binary_field"
...
--...
>>> '\x01\x02' in doc
True
>>> method = service_root.get_method('post', 'text/unknown')
>>> method.build_representation(field="value")
Traceback (most recent call last):
...
ValueError: Unsupported media type: 'text/unknown'
Options
=======
Some parameters take values from a predefined list of options.
>>> 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']
Such parameters cannot take values that are not in the list.
>>> 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"
Error conditions
================
You'll get None if you try to look up a nonexistent resource.
>>> print(wadl.get_resource_by_path('nosuchresource'))
None
You'll get an exception if you try to look up a nonexistent resource
type.
>>> print(wadl.get_resource_type('#nosuchtype'))
Traceback (most recent call last):
KeyError: 'No such XML ID: "#nosuchtype"'
You'll get None if you try to look up a method whose parameters don't
match any defined method.
>>> print(bound_limi.get_method(
... 'post', representation_params={ 'foo' : 'bar' }))
None
.. cleanup
>>> for stream in cleanups:
... stream.close()
.. toctree::
:glob:
*
docs/*
wadllib-1.3.2/src/wadllib/iso_strptime.py 0000664 0001750 0001750 00000005511 12112731513 020713 0 ustar barry barry 0000000 0000000 # Copyright 2009 Canonical Ltd. All rights reserved.
# This file is part of wadllib.
#
# wadllib is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# wadllib is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with wadllib. If not, see .
"""
Parser for ISO 8601 time strings
================================
>>> d = iso_strptime("2008-01-07T05:30:30.345323+03:00")
>>> d
datetime.datetime(2008, 1, 7, 5, 30, 30, 345323, tzinfo=TimeZone(10800))
>>> d.timetuple()
(2008, 1, 7, 5, 30, 30, 0, 7, 0)
>>> d.utctimetuple()
(2008, 1, 7, 2, 30, 30, 0, 7, 0)
>>> iso_strptime("2008-01-07T05:30:30.345323-03:00")
datetime.datetime(2008, 1, 7, 5, 30, 30, 345323, tzinfo=TimeZone(-10800))
>>> iso_strptime("2008-01-07T05:30:30.345323")
datetime.datetime(2008, 1, 7, 5, 30, 30, 345323)
>>> iso_strptime("2008-01-07T05:30:30")
datetime.datetime(2008, 1, 7, 5, 30, 30)
>>> iso_strptime("2008-01-07T05:30:30+02:00")
datetime.datetime(2008, 1, 7, 5, 30, 30, tzinfo=TimeZone(7200))
"""
import re
import datetime
RE_TIME = re.compile(r"""^
# pattern matching date
(?P\d{4})\-(?P\d{2})\-(?P\d{2})
# separator
T
# pattern matching time
(?P\d{2})\:(?P\d{2})\:(?P\d{2})
# pattern matching optional microseconds
(\.(?P\d{6}))?
# pattern matching optional timezone offset
(?P[\-\+]\d{2}\:\d{2})?
$""", re.VERBOSE)
class TimeZone(datetime.tzinfo):
def __init__(self, tz_string):
hours, minutes = tz_string.lstrip("-+").split(":")
self.stdoffset = datetime.timedelta(hours=int(hours),
minutes=int(minutes))
if tz_string.startswith("-"):
self.stdoffset *= -1
def __repr__(self):
return "TimeZone(%s)" % (
self.stdoffset.days*24*60*60 + self.stdoffset.seconds)
def utcoffset(self, dt):
return self.stdoffset
def dst(self, dt):
return datetime.timedelta(0)
def iso_strptime(time_str):
x = RE_TIME.match(time_str)
if not x:
raise ValueError
d = datetime.datetime(int(x.group("year")), int(x.group("month")),
int(x.group("day")), int(x.group("hour")), int(x.group("minutes")),
int(x.group("seconds")))
if x.group("microseconds"):
d = d.replace(microsecond=int(x.group("microseconds")))
if x.group("tz_offset"):
d = d.replace(tzinfo=TimeZone(x.group("tz_offset")))
return d
wadllib-1.3.2/src/wadllib/NEWS.txt 0000664 0001750 0001750 00000004625 12112755512 017147 0 ustar barry barry 0000000 0000000 ================
NEWS for wadllib
================
1.3.2 (2013-02-25)
==================
- Impose sort order to avoid test failures due to hash randomization.
LP: #1132125
- Be sure to close streams opened by pkg_resources.resource_stream() to avoid
test suite complaints.
1.3.1 (2012-03-22)
==================
- Correct the double pass through _from_string causing datetime issues
1.3.0 (2012-01-27)
==================
- Add Python 3 compatibility
- Add the ability to inspect links before following them.
- Ensure that the sample data is packaged.
1.2.0 (2011-02-03)
==================
- It's now possible to examine a link before following it, to see
whether it has a WADL description or whether it needs to be fetched
with a general HTTP client.
- It's now possible to iterate over a resource's Parameter objects
with the .parameters() method.
1.1.8 (2010-10-27)
==================
- This revision contains no code changes, but the build system was
changed (yet again). This time to include the version.txt file
used by setup.py.
1.1.7 (2010-10-26)
==================
- This revision contains no code changes, but the build system was
changed (again) to include the sample data used in tests.
1.1.6 (2010-10-21)
==================
- This revision contains no code changes, but the build system was
changed to include the sample data used in tests.
1.1.5 (2010-05-04)
==================
- Fixed a bug (Launchpad bug 274074) that prevented the lookup of
parameter values in resources associated directly with a
representation definition (rather than a resource type with a
representation definition). Bug fix provided by James Westby.
1.1.4 (2009-09-15)
==================
- Fixed a bug that crashed wadllib unless all parameters of a
multipart representation were provided.
1.1.3 (2009-08-26)
==================
- Remove unnecessary build dependencies.
- Add missing dependencies to setup file.
- Remove sys.path hack from setup.py.
1.1.2 (2009-08-20)
==================
- Consistently handle different versions of simplejson.
1.1.1 (2009-07-14)
==================
- Make wadllib aware of the tags that go beneath tags.
1.1 (2009-07-09)
================
- Make wadllib capable of recognizing and generating
multipart/form-data representations, including representations that
incorporate binary parameters.
1.0 (2009-03-23)
================
- Initial release on PyPI
wadllib-1.3.2/src/wadllib/version.txt 0000664 0001750 0001750 00000000006 12112755627 020054 0 ustar barry barry 0000000 0000000 1.3.2
wadllib-1.3.2/src/wadllib/__init__.py 0000664 0001750 0001750 00000002044 12112731513 017727 0 ustar barry barry 0000000 0000000 # Copyright 2008-2009 Canonical Ltd. All rights reserved.
# This file is part of wadllib.
#
# wadllib is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# wadllib is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with wadllib. If not, see
# .
import sys
import pkg_resources
__version__ = pkg_resources.resource_string(
"wadllib", "version.txt").strip()
if sys.version_info[0] >= 3:
_string_types = str
def _make_unicode(b):
if hasattr(b, 'decode'):
return b.decode()
else:
return str(b)
else:
_string_types = basestring
_make_unicode = unicode
wadllib-1.3.2/src/wadllib/application.py 0000664 0001750 0001750 00000130440 12112755512 020502 0 ustar barry barry 0000000 0000000 # Copyright 2008-2013 Canonical Ltd. All rights reserved.
# This file is part of wadllib.
#
# wadllib is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# wadllib is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with wadllib. If not, see .
"""Navigate the resources exposed by a web service.
The wadllib library helps a web client navigate the resources
exposed by a web service. The service defines its resources in a
single WADL file. wadllib parses this file and gives access to the
resources defined inside. The client code can see the capabilities of
a given resource and make the corresponding HTTP requests.
If a request returns a representation of the resource, the client can
bind the string representation to the wadllib Resource object.
"""
__metaclass__ = type
__all__ = [
'Application',
'Link',
'Method',
'NoBoundRepresentationError',
'Parameter',
'RepresentationDefinition',
'ResponseDefinition',
'Resource',
'ResourceType',
'WADLError',
]
try:
# Make sure to try the Python 2 form first; we don't want to use
# io.StringIO in Python 2, because it is strict about only accepting
# unicode input.
from cStringIO import StringIO as BytesIO
except ImportError:
from io import BytesIO
import datetime
try:
from email.mime.multipart import MIMEMultipart
from email.mime.nonmultipart import MIMENonMultipart
except ImportError:
# We must be on 2.4.
from email.MIMEMultipart import MIMEMultipart
from email.MIMENonMultipart import MIMENonMultipart
import time
try:
from urllib.parse import urlencode
except ImportError:
from urllib import urlencode
try:
import simplejson as json
except ImportError:
import json
try:
import xml.etree.cElementTree as ET
except ImportError:
try:
import cElementTree as ET
except ImportError:
import elementtree.ElementTree as ET
from lazr.uri import URI, merge
from wadllib import (
_make_unicode,
_string_types,
)
from wadllib.iso_strptime import iso_strptime
NS_MAP = "xmlns:map"
XML_SCHEMA_NS_URI = 'http://www.w3.org/2001/XMLSchema'
def wadl_tag(tag_name):
"""Scope a tag name with the WADL namespace."""
return '{http://research.sun.com/wadl/2006/10}' + tag_name
def wadl_xpath(tag_name):
"""Turn a tag name into an XPath path."""
return './' + wadl_tag(tag_name)
def _merge_dicts(*dicts):
"""Merge any number of dictionaries, some of which may be None."""
final = {}
for dict in dicts:
if dict is not None:
final.update(dict)
return final
class WADLError(Exception):
"""An exception having to do with the state of the WADL application."""
pass
class NoBoundRepresentationError(WADLError):
"""An unbound resource was used where wadllib expected a bound resource.
To obtain the value of a resource's parameter, you first must bind
the resource to a representation. Otherwise the resource has no
idea what the value is and doesn't even know if you've given it a
parameter name that makes sense.
"""
class UnsupportedMediaTypeError(WADLError):
"""A media type was given that's not supported in this context.
A resource can only be bound to media types it has representations
of.
"""
class WADLBase(object):
"""A base class for objects that contain WADL-derived information."""
class HasParametersMixin:
"""A mixin class for objects that have associated Parameter objects."""
def params(self, styles, resource=None):
"""Find subsidiary parameters that have the given styles."""
if resource is None:
resource = self.resource
if resource is None:
raise ValueError("Could not find any particular resource")
if self.tag is None:
return []
param_tags = self.tag.findall(wadl_xpath('param'))
if param_tags is None:
return []
return [Parameter(resource, param_tag)
for param_tag in param_tags
if param_tag.attrib.get('style') in styles]
def validate_param_values(self, params, param_values,
enforce_completeness=True, **kw_param_values):
"""Make sure the given valueset is valid.
A valueset might be invalid because it contradicts a fixed
value or (if enforce_completeness is True) because it lacks a
required value.
:param params: A list of Parameter objects.
:param param_values: A dictionary of parameter values. May include
paramters whose names are not valid Python identifiers.
:param enforce_completeness: If True, this method will raise
an exception when the given value set lacks a value for a
required parameter.
:param kw_param_values: A dictionary of parameter values.
:return: A dictionary of validated parameter values.
"""
param_values = _merge_dicts(param_values, kw_param_values)
validated_values = {}
for param in params:
name = param.name
if param.fixed_value is not None:
if (name in param_values
and param_values[name] != param.fixed_value):
raise ValueError(("Value '%s' for parameter '%s' "
"conflicts with fixed value '%s'")
% (param_values[name], name,
param.fixed_value))
param_values[name] = param.fixed_value
options = [option.value for option in param.options]
if (len(options) > 0 and name in param_values
and param_values[name] not in options):
raise ValueError(("Invalid value '%s' for parameter '%s': "
'valid values are: "%s"') % (
param_values[name], name, '", "'.join(options)))
if (enforce_completeness and param.is_required
and not name in param_values):
raise ValueError("No value for required parameter '%s'"
% name)
if name in param_values:
validated_values[name] = param_values[name]
del param_values[name]
if len(param_values) > 0:
raise ValueError("Unrecognized parameter(s): '%s'"
% "', '".join(param_values.keys()))
return validated_values
class WADLResolvableDefinition(WADLBase):
"""A base class for objects whose definitions may be references."""
def __init__(self, application):
"""Initialize with a WADL application.
:param application: A WADLDefinition. Relative links are
assumed to be relative to this object's URL.
"""
self._definition = None
self.application = application
def resolve_definition(self):
"""Return the definition of this object, wherever it is.
Resource is a good example. A WADL tag
may contain a large number of nested tags describing a
resource, or it may just contain a 'type' attribute that
references a which contains those same
tags. Resource.resolve_definition() will return the original
Resource object in the first case, and a
ResourceType object in the second case.
"""
if self._definition is not None:
return self._definition
object_url = self._get_definition_url()
if object_url is None:
# The object contains its own definition.
# XXX leonardr 2008-05-28:
# This code path is not tested in Launchpad.
self._definition = self
return self
# The object makes reference to some other object. Resolve
# its URL and return it.
xml_id = self.application.lookup_xml_id(object_url)
definition = self._definition_factory(xml_id)
if definition is None:
# XXX leonardr 2008-06-
# This code path is not tested in Launchpad.
# It requires an invalid WADL file that makes
# a reference to a nonexistent tag within the
# same WADL file.
raise KeyError('No such XML ID: "%s"' % object_url)
self._definition = definition
return definition
def _definition_factory(self, id):
"""Transform an XML ID into a wadllib wrapper object.
Which kind of object it is depends on the subclass.
"""
raise NotImplementedError()
def _get_definition_url(self):
"""Find the URL that identifies an external reference.
How to do this depends on the subclass.
"""
raise NotImplementedError()
class Resource(WADLResolvableDefinition):
"""A resource, possibly bound to a representation."""
def __init__(self, application, url, resource_type,
representation=None, media_type=None,
representation_needs_processing=True,
representation_definition=None):
"""
:param application: A WADLApplication.
:param url: The URL to this resource.
:param resource_type: An ElementTree or tag.
:param representation: A string representation.
:param media_type: The media type of the representation.
:param representation_needs_processing: Set to False if the
'representation' parameter should be used as
is. Otherwise, it will be transformed from a string into
an appropriate Python data structure, depending on its
media type.
:param representation_definition: A RepresentationDefinition
object describing the structure of this
representation. Used in cases when the representation
isn't the result of sending a standard GET to the
resource.
"""
super(Resource, self).__init__(application)
self._url = url
if isinstance(resource_type, _string_types):
# We were passed the URL to a resource type. Look up the
# type object itself
self.tag = self.application.get_resource_type(resource_type).tag
else:
# We were passed an XML tag that describes a resource or
# resource type.
self.tag = resource_type
self.representation = None
if representation is not None:
if media_type == 'application/json':
if representation_needs_processing:
self.representation = json.loads(
_make_unicode(representation))
else:
self.representation = representation
else:
raise UnsupportedMediaTypeError(
"This resource doesn't define a representation for "
"media type %s" % media_type)
self.media_type = media_type
if representation is not None:
if representation_definition is not None:
self.representation_definition = representation_definition
else:
self.representation_definition = (
self.get_representation_definition(self.media_type))
@property
def url(self):
"""Return the URL to this resource."""
return self._url
@property
def type_url(self):
"""Return the URL to the type definition for this resource, if any."""
if self.tag is None:
return None
url = self.tag.attrib.get('type')
if url is not None:
# This resource is defined in the WADL file.
return url
type_id = self.tag.attrib.get('id')
if type_id is not None:
# This resource was obtained by following a link.
base = URI(self.application.markup_url).ensureSlash()
return str(base) + '#' + type_id
# This resource does not have any associated resource type.
return None
@property
def id(self):
"""Return the ID of this resource."""
return self.tag.attrib['id']
def bind(self, representation, media_type='application/json',
representation_needs_processing=True,
representation_definition=None):
"""Bind the resource to a representation of that resource.
:param representation: A string representation
:param media_type: The media type of the representation.
:param representation_needs_processing: Set to False if the
'representation' parameter should be used as
is.
:param representation_definition: A RepresentationDefinition
object describing the structure of this
representation. Used in cases when the representation
isn't the result of sending a standard GET to the
resource.
:return: A Resource bound to a particular representation.
"""
return Resource(self.application, self.url, self.tag,
representation, media_type,
representation_needs_processing,
representation_definition)
def get_representation_definition(self, media_type):
"""Get a description of one of this resource's representations."""
default_get_response = self.get_method('GET').response
for representation in default_get_response:
representation_tag = representation.resolve_definition().tag
if representation_tag.attrib.get('mediaType') == media_type:
return representation
raise UnsupportedMediaTypeError("No definition for representation "
"with media type %s." % media_type)
def get_method(self, http_method=None, media_type=None, query_params=None,
representation_params=None):
"""Look up one of this resource's methods by HTTP method.
:param http_method: The HTTP method used to invoke the desired
method. Case-insensitive and optional.
:param media_type: The media type of the representation
accepted by the method. Optional.
:param query_params: The names and values of any fixed query
parameters used to distinguish between
two methods that use the same HTTP
method. Optional.
:param representation_params: The names and values of any
fixed representation parameters used to
distinguish between two methods that use
the same HTTP method and have the same
media type. Optional.
:return: A MethodDefinition, or None if there's no definition
that fits the given constraints.
"""
for method_tag in self._method_tag_iter():
name = method_tag.attrib.get('name', '').lower()
if http_method is None or name == http_method.lower():
method = Method(self, method_tag)
if method.is_described_by(media_type, query_params,
representation_params):
return method
return None
def parameters(self, media_type=None):
"""A list of this resource's parameters.
:param media_type: Media type of the representation definition
whose parameters are being named. Must be present unless
this resource is bound to a representation.
:raise NoBoundRepresentationError: If this resource is not
bound to a representation and media_type was not provided.
"""
return self._find_representation_definition(
media_type).params(self)
def parameter_names(self, media_type=None):
"""A list naming this resource's parameters.
:param media_type: Media type of the representation definition
whose parameters are being named. Must be present unless
this resource is bound to a representation.
:raise NoBoundRepresentationError: If this resource is not
bound to a representation and media_type was not provided.
"""
return self._find_representation_definition(
media_type).parameter_names(self)
@property
def method_iter(self):
"""An iterator over the methods defined on this resource."""
for method_tag in self._method_tag_iter():
yield Method(self, method_tag)
def get_parameter(self, param_name, media_type=None):
"""Find a parameter within a representation definition.
:param param_name: Name of the parameter to find.
:param media_type: Media type of the representation definition
whose parameters are being named. Must be present unless
this resource is bound to a representation.
:raise NoBoundRepresentationError: If this resource is not
bound to a representation and media_type was not provided.
"""
definition = self._find_representation_definition(media_type)
representation_tag = definition.tag
for param_tag in representation_tag.findall(wadl_xpath('param')):
if param_tag.attrib.get('name') == param_name:
return Parameter(self, param_tag)
return None
def get_parameter_value(self, parameter):
"""Find the value of a parameter, given the Parameter object.
:raise ValueError: If the parameter value can't be converted into
its defined type.
"""
if self.representation is None:
raise NoBoundRepresentationError(
"Resource is not bound to any representation.")
if self.media_type == 'application/json':
# XXX leonardr 2008-05-28 A real JSONPath implementation
# should go here. It should execute tag.attrib['path']
# against the JSON representation.
#
# Right now the implementation assumes the JSON
# representation is a hash and treats tag.attrib['name'] as a
# key into the hash.
if parameter.style != 'plain':
raise NotImplementedError(
"Don't know how to find value for a parameter of "
"type %s." % parameter.style)
value = self.representation[parameter.name]
if value is not None:
namespace_url, data_type = self._dereference_namespace(
parameter.tag, parameter.type)
if (namespace_url == XML_SCHEMA_NS_URI
and data_type in ['dateTime', 'date']):
try:
# Parse it as an ISO 8601 date and time.
value = iso_strptime(value)
except ValueError:
# Parse it as an ISO 8601 date.
try:
value = datetime.datetime(
*(time.strptime(value, "%Y-%m-%d")[0:6]))
except ValueError:
# Raise an unadorned ValueError so the client
# can treat the value as a string if they
# want.
raise ValueError(value)
return value
raise NotImplementedError("Path traversal not implemented for "
"a representation of media type %s."
% self.media_type)
def _dereference_namespace(self, tag, value):
"""Splits a value into namespace URI and value.
:param tag: A tag to use as context when mapping namespace
names to URIs.
"""
if value is not None and ':' in value:
namespace, value = value.split(':', 1)
else:
namespace = ''
ns_map = tag.get(NS_MAP)
namespace_url = ns_map.get(namespace, None)
return namespace_url, value
def _definition_factory(self, id):
"""Given an ID, find a ResourceType for that ID."""
return self.application.resource_types.get(id)
def _get_definition_url(self):
"""Return the URL that shows where a resource is 'really' defined.
If a resource's capabilities are defined by reference, the
tag's 'type' attribute will contain the URL to the
that defines them.
"""
return self.tag.attrib.get('type')
def _find_representation_definition(self, media_type=None):
"""Get the most appropriate representation definition.
If media_type is provided, the most appropriate definition is
the definition of the representation of that media type.
If this resource is bound to a representation, the most
appropriate definition is the definition of that
representation. Otherwise, the most appropriate definition is
the definition of the representation served in response to a
standard GET.
:param media_type: Media type of the definition to find. Must
be present unless the resource is bound to a
representation.
:raise NoBoundRepresentationError: If this resource is not
bound to a representation and media_type was not provided.
:return: A RepresentationDefinition
"""
if self.representation is not None:
# We know that when this object was created, a
# representation definition was either looked up, or
# directly passed in along with the representation.
definition = self.representation_definition.resolve_definition()
elif media_type is not None:
definition = self.get_representation_definition(media_type)
else:
raise NoBoundRepresentationError(
"Resource is not bound to any representation, and no media "
"media type was specified.")
return definition.resolve_definition()
def _method_tag_iter(self):
"""Iterate over this resource's tags."""
definition = self.resolve_definition().tag
for method_tag in definition.findall(wadl_xpath('method')):
yield method_tag
class Method(WADLBase):
"""A wrapper around an XML tag.
"""
def __init__(self, resource, method_tag):
"""Initialize with a tag.
:param method_tag: An ElementTree tag.
"""
self.resource = resource
self.application = self.resource.application
self.tag = method_tag
@property
def request(self):
"""Return the definition of a request that invokes the WADL method."""
return RequestDefinition(self, self.tag.find(wadl_xpath('request')))
@property
def response(self):
"""Return the definition of the response to the WADL method."""
return ResponseDefinition(self.resource,
self.tag.find(wadl_xpath('response')))
@property
def id(self):
"""The XML ID of the WADL method definition."""
return self.tag.attrib.get('id')
@property
def name(self):
"""The name of the WADL method definition.
This is also the name of the HTTP method (GET, POST, etc.)
that should be used to invoke the WADL method.
"""
return self.tag.attrib.get('name').lower()
def build_request_url(self, param_values=None, **kw_param_values):
"""Return the request URL to use to invoke this method."""
return self.request.build_url(param_values, **kw_param_values)
def build_representation(self, media_type=None,
param_values=None, **kw_param_values):
"""Build a representation to be sent when invoking this method.
:return: A 2-tuple of (media_type, representation).
"""
return self.request.representation(
media_type, param_values, **kw_param_values)
def is_described_by(self, media_type=None, query_values=None,
representation_values=None):
"""Returns true if this method fits the given constraints.
:param media_type: The method must accept this media type as a
representation.
:param query_values: These key-value pairs must be acceptable
as values for this method's query
parameters. This need not be a complete set
of parameters acceptable to the method.
:param representation_values: These key-value pairs must be
acceptable as values for this method's
representation parameters. Again, this need
not be a complete set of parameters
acceptable to the method.
"""
representation = None
if media_type is not None:
representation = self.request.get_representation_definition(
media_type)
if representation is None:
return False
if query_values is not None and len(query_values) > 0:
request = self.request
if request is None:
# This method takes no special request
# parameters, so it can't match.
return False
try:
request.validate_param_values(
request.query_params, query_values, False)
except ValueError:
return False
# At this point we know the media type and query values match.
if (representation_values is None
or len(representation_values) == 0):
return True
if representation is not None:
return representation.is_described_by(
representation_values)
for representation in self.request.representations:
try:
representation.validate_param_values(
representation.params(self.resource),
representation_values, False)
return True
except ValueError:
pass
return False
class RequestDefinition(WADLBase, HasParametersMixin):
"""A wrapper around the description of the request invoking a method."""
def __init__(self, method, request_tag):
"""Initialize with a tag.
:param resource: The resource to which this request can be sent.
:param request_tag: An ElementTree tag.
"""
self.method = method
self.resource = self.method.resource
self.application = self.resource.application
self.tag = request_tag
@property
def query_params(self):
"""Return the query parameters for this method."""
return self.params(['query'])
@property
def representations(self):
for definition in self.tag.findall(wadl_xpath('representation')):
yield RepresentationDefinition(
self.application, self.resource, definition)
def get_representation_definition(self, media_type=None):
"""Return the appropriate representation definition."""
for representation in self.representations:
if media_type is None or representation.media_type == media_type:
return representation
return None
def representation(self, media_type=None, param_values=None,
**kw_param_values):
"""Build a representation to be sent along with this request.
:return: A 2-tuple of (media_type, representation).
"""
definition = self.get_representation_definition(media_type)
if definition is None:
raise TypeError("Cannot build representation of media type %s"
% media_type)
return definition.bind(param_values, **kw_param_values)
def build_url(self, param_values=None, **kw_param_values):
"""Return the request URL to use to invoke this method."""
validated_values = self.validate_param_values(
self.query_params, param_values, **kw_param_values)
url = self.resource.url
if len(validated_values) > 0:
if '?' in url:
append = '&'
else:
append = '?'
url += append + urlencode(sorted(validated_values.items()))
return url
class ResponseDefinition(HasParametersMixin):
"""A wrapper around the description of a response to a method."""
# XXX leonardr 2008-05-29 it would be nice to have
# ResponseDefinitions for POST operations and nonstandard GET
# operations say what representations and/or status codes you get
# back. Getting this to work with Launchpad requires work on the
# Launchpad side.
def __init__(self, resource, response_tag, headers=None):
"""Initialize with a tag.
:param response_tag: An ElementTree tag.
"""
self.application = resource.application
self.resource = resource
self.tag = response_tag
self.headers = headers
def __iter__(self):
"""Get an iterator over the representation definitions.
These are the representations returned in response to an
invocation of this method.
"""
path = wadl_xpath('representation')
for representation_tag in self.tag.findall(path):
yield RepresentationDefinition(
self.resource.application, self.resource, representation_tag)
def bind(self, headers):
"""Bind the response to a set of HTTP headers.
A WADL response can have associated header parameters, but no
other kind.
"""
return ResponseDefinition(self.resource, self.tag, headers)
def get_parameter(self, param_name):
"""Find a header parameter within the response."""
for param_tag in self.tag.findall(wadl_xpath('param')):
if (param_tag.attrib.get('name') == param_name
and param_tag.attrib.get('style') == 'header'):
return Parameter(self, param_tag)
return None
def get_parameter_value(self, parameter):
"""Find the value of a parameter, given the Parameter object."""
if self.headers is None:
raise NoBoundRepresentationError(
"Response object is not bound to any headers.")
if parameter.style != 'header':
raise NotImplementedError(
"Don't know how to find value for a parameter of "
"type %s." % parameter.style)
return self.headers.get(parameter.name)
def get_representation_definition(self, media_type):
"""Get one of the possible representations of the response."""
if self.tag is None:
return None
for representation in self:
if representation.media_type == media_type:
return representation
return None
class RepresentationDefinition(WADLResolvableDefinition, HasParametersMixin):
"""A definition of the structure of a representation."""
def __init__(self, application, resource, representation_tag):
super(RepresentationDefinition, self).__init__(application)
self.resource = resource
self.tag = representation_tag
def params(self, resource):
return super(RepresentationDefinition, self).params(
['query', 'plain'], resource)
def parameter_names(self, resource):
"""Return the names of all parameters."""
return [param.name for param in self.params(resource)]
@property
def media_type(self):
"""The media type of the representation described here."""
return self.resolve_definition().tag.attrib['mediaType']
def bind(self, param_values, **kw_param_values):
"""Bind the definition to parameter values, creating a document.
:return: A 2-tuple (media_type, document).
"""
definition = self.resolve_definition()
params = definition.params(self.resource)
validated_values = self.validate_param_values(
params, param_values, **kw_param_values)
media_type = self.media_type
if media_type == 'application/x-www-form-urlencoded':
doc = urlencode(sorted(validated_values.items()))
elif media_type == 'multipart/form-data':
outer = MIMEMultipart()
outer.set_type('multipart/form-data')
missing = object()
for param in params:
value = validated_values.get(param.name, missing)
if value is not missing:
if param.type == 'binary':
maintype, subtype = 'application', 'octet-stream'
params = {}
else:
maintype, subtype = 'text', 'plain'
params = {'charset' : 'utf-8'}
inner = MIMENonMultipart(maintype, subtype, **params)
inner.set_payload(value)
inner['Content-Disposition'] = (
'form-data; name="%s"' % param.name)
outer.attach(inner)
doc = str(outer)
# Chop off the 'From' line, which only makes sense in an
# email. (Python 3 does not include it.)
if doc.startswith('From '):
doc = doc[doc.find('\n')+1:]
media_type = (outer.get_content_type() +
'; boundary="%s"' % outer.get_boundary())
elif media_type == 'application/json':
doc = json.dumps(validated_values)
else:
raise ValueError("Unsupported media type: '%s'" % media_type)
return media_type, doc
def _definition_factory(self, id):
"""Turn a representation ID into a RepresentationDefinition."""
return self.application.representation_definitions.get(id)
def _get_definition_url(self):
"""Find the URL containing the representation's 'real' definition.
If a representation's structure is defined by reference, the
tag's 'href' attribute will contain the URL
to the that defines the structure.
"""
return self.tag.attrib.get('href')
class Parameter(WADLBase):
"""One of the parameters of a representation definition."""
def __init__(self, value_container, tag):
"""Initialize with respect to a value container.
:param value_container: Usually the resource whose representation
has this parameter. If the resource is bound to a representation,
you'll be able to find the value of this parameter in the
representation. This may also be a server response whose headers
define a value for this parameter.
:tag: The ElementTree tag for this parameter.
"""
self.application = value_container.application
self.value_container = value_container
self.tag = tag
@property
def name(self):
"""The name of this parameter."""
return self.tag.attrib.get('name')
@property
def style(self):
"""The style of this parameter."""
return self.tag.attrib.get('style')
@property
def type(self):
"""The XSD type of this parameter."""
return self.tag.attrib.get('type')
@property
def fixed_value(self):
"""The value to which this parameter is fixed, if any.
A fixed parameter must be present in invocations of a WADL
method, and it must have a particular value. This is commonly
used to designate one parameter as containing the name of the
server-side operation to be invoked.
"""
return self.tag.attrib.get('fixed')
@property
def is_required(self):
"""Whether or not a value for this parameter is required."""
return (self.tag.attrib.get('required', 'false').lower()
in ['1', 'true'])
def get_value(self):
"""The value of this parameter in the bound representation/headers.
:raise NoBoundRepresentationError: If this parameter's value
container is not bound to a representation or a set of
headers.
"""
return self.value_container.get_parameter_value(self)
@property
def options(self):
"""Return the set of acceptable values for this parameter."""
return [Option(self, option_tag)
for option_tag in self.tag.findall(wadl_xpath('option'))]
@property
def link(self):
"""Get the link to another resource.
The link may be examined and, if its type is of a known WADL
description, it may be followed.
:return: A Link object, or None.
"""
link_tag = self.tag.find(wadl_xpath('link'))
if link_tag is None:
return None
return Link(self, link_tag)
@property
def linked_resource(self):
"""Follow a link from this parameter to a new resource.
This only works for parameters whose WADL definition includes a
tag that points to a known WADL description.
:return: A Resource object for the resource at the other end
of the link.
"""
link = self.link
if link is None:
raise ValueError("This parameter isn't a link to anything.")
return link.follow
class Option(WADLBase):
"""One of a set of possible values for a parameter."""
def __init__(self, parameter, option_tag):
"""Initialize the option.
:param parameter: A Parameter.
:param link_tag: An ElementTree tag.
"""
self.parameter = parameter
self.tag = option_tag
@property
def value(self):
return self.tag.attrib.get('value')
class Link(WADLResolvableDefinition):
"""A link from one resource to another.
Calling resolve_definition() on a Link will give you a Resource for the
type of resource linked to. An alias for this is 'follow'.
"""
def __init__(self, parameter, link_tag):
"""Initialize the link.
:param parameter: A Parameter.
:param link_tag: An ElementTree tag.
"""
super(Link, self).__init__(parameter.application)
self.parameter = parameter
self.tag = link_tag
@property
def follow(self):
"""Follow the link to another Resource."""
if not self.can_follow:
raise WADLError("Cannot follow a link when the target has no "
"WADL description. Try using a general HTTP "
"client instead.")
return self.resolve_definition()
@property
def can_follow(self):
"""Can this link be followed within wadllib?
wadllib can follow a link if it points to a resource that has
a WADL definition.
"""
try:
definition_url = self._get_definition_url()
except WADLError:
return False
return True
def _definition_factory(self, id):
"""Turn a resource type ID into a ResourceType."""
return Resource(
self.application, self.parameter.get_value(),
self.application.resource_types.get(id).tag)
def _get_definition_url(self):
"""Find the URL containing the definition ."""
type = self.tag.attrib.get('resource_type')
if type is None:
raise WADLError("Parameter is a link, but not to a resource "
"with a known WADL description.")
return type
class ResourceType(WADLBase):
"""A wrapper around an XML tag."""
def __init__(self, resource_type_tag):
"""Initialize with a tag.
:param resource_type_tag: An ElementTree tag.
"""
self.tag = resource_type_tag
class Application(WADLBase):
"""A WADL document made programmatically accessible."""
def __init__(self, markup_url, markup):
"""Parse WADL and find the most important parts of the document.
:param markup_url: The URL from which this document was obtained.
:param markup: The WADL markup itself, or an open filehandle to it.
"""
self.markup_url = markup_url
if hasattr(markup, 'read'):
self.doc = self._from_stream(markup)
else:
self.doc = self._from_string(markup)
self.resources = self.doc.find(wadl_xpath('resources'))
self.resource_base = self.resources.attrib.get('base')
self.representation_definitions = {}
self.resource_types = {}
for representation in self.doc.findall(wadl_xpath('representation')):
id = representation.attrib.get('id')
if id is not None:
definition = RepresentationDefinition(
self, None, representation)
self.representation_definitions[id] = definition
for resource_type in self.doc.findall(wadl_xpath('resource_type')):
id = resource_type.attrib['id']
self.resource_types[id] = ResourceType(resource_type)
def _from_stream(self, stream):
"""Turns markup into a document.
Just a wrapper around ElementTree which keeps track of namespaces.
"""
events = "start", "start-ns", "end-ns"
root = None
ns_map = []
for event, elem in ET.iterparse(stream, events):
if event == "start-ns":
ns_map.append(elem)
elif event == "end-ns":
ns_map.pop()
elif event == "start":
if root is None:
root = elem
elem.set(NS_MAP, dict(ns_map))
return ET.ElementTree(root)
def _from_string(self, markup):
"""Turns markup into a document."""
return self._from_stream(BytesIO(markup))
def get_resource_type(self, resource_type_url):
"""Retrieve a resource type by the URL of its description."""
xml_id = self.lookup_xml_id(resource_type_url)
resource_type = self.resource_types.get(xml_id)
if resource_type is None:
raise KeyError('No such XML ID: "%s"' % resource_type_url)
return resource_type
def lookup_xml_id(self, url):
"""A helper method for locating a part of a WADL document.
:param url: The URL (with anchor) of the desired part of the
WADL document.
:return: The XML ID corresponding to the anchor.
"""
markup_uri = URI(self.markup_url).ensureNoSlash()
markup_uri.fragment = None
if url.startswith('http'):
# It's an absolute URI.
this_uri = URI(url).ensureNoSlash()
else:
# It's a relative URI.
this_uri = markup_uri.resolve(url)
possible_xml_id = this_uri.fragment
this_uri.fragment = None
if this_uri == markup_uri:
# The URL pointed elsewhere within the same WADL document.
# Return its fragment.
return possible_xml_id
# XXX leonardr 2008-05-28:
# This needs to be implemented eventually for Launchpad so
# that a script using this client can navigate from a WADL
# representation of a non-root resource to its definition at
# the server root.
raise NotImplementedError("Can't look up definition in another "
"url (%s)" % url)
def get_resource_by_path(self, path):
"""Locate one of the resources described by this document.
:param path: The path to the resource.
"""
# XXX leonardr 2008-05-27 This method only finds top-level
# resources. That's all we need for Launchpad because we don't
# define nested resources yet.
matching = [resource for resource in self.resources
if resource.attrib['path'] == path]
if len(matching) < 1:
return None
if len(matching) > 1:
raise WADLError("More than one resource defined with path %s"
% path)
return Resource(
self, merge(self.resource_base, path, True), matching[0])
wadllib-1.3.2/src/wadllib/tests/ 0000775 0001750 0001750 00000000000 12112756624 016772 5 ustar barry barry 0000000 0000000 wadllib-1.3.2/src/wadllib/tests/test_docs.py 0000664 0001750 0001750 00000003171 12112731513 021323 0 ustar barry barry 0000000 0000000 # Copyright 2009 Canonical Ltd. All rights reserved.
#
# This file is part of wadllib
#
# wadllib is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, version 3 of the License.
#
# wadllib is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with wadllib. If not, see .
"""Test harness."""
__metaclass__ = type
__all__ = [
'additional_tests',
]
import atexit
import doctest
import os
import pkg_resources
import unittest
DOCTEST_FLAGS = (
doctest.ELLIPSIS |
doctest.NORMALIZE_WHITESPACE |
doctest.REPORT_NDIFF)
def additional_tests():
doctest_files = [
os.path.abspath(
pkg_resources.resource_filename('wadllib', 'README.txt'))]
if pkg_resources.resource_exists('wadllib', 'docs'):
for name in pkg_resources.resource_listdir('wadllib', 'docs'):
if name.endswith('.txt'):
doctest_files.append(
os.path.abspath(
pkg_resources.resource_filename(
'wadllib', 'docs/%s' % name)))
kwargs = dict(module_relative=False, optionflags=DOCTEST_FLAGS)
atexit.register(pkg_resources.cleanup_resources)
return unittest.TestSuite((
doctest.DocFileSuite(*doctest_files, **kwargs)))
wadllib-1.3.2/src/wadllib/tests/__init__.py 0000664 0001750 0001750 00000001260 12112731513 021070 0 ustar barry barry 0000000 0000000 # Copyright 2008-2009 Canonical Ltd. All rights reserved.
# This file is part of wadllib.
#
# wadllib is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# wadllib is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with wadllib. If not, see
# .
wadllib-1.3.2/src/wadllib/tests/data/ 0000775 0001750 0001750 00000000000 12112756624 017703 5 ustar barry barry 0000000 0000000 wadllib-1.3.2/src/wadllib/tests/data/links-wadl.xml 0000664 0001750 0001750 00000002004 12112731513 022454 0 ustar barry barry 0000000 0000000
wadllib-1.3.2/src/wadllib/tests/data/personset-page2.json 0000664 0001750 0001750 00000031050 12112731513 023601 0 ustar barry barry 0000000 0000000 {"total_size": 63, "prev_collection_link": "http:\/\/api.launchpad.dev\/beta\/people?ws.start=0&ws.size=5", "start": 5, "next_collection_link": "http:\/\/api.launchpad.dev\/beta\/people?ws.start=10&ws.size=5", "entries": [{"languages_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/languages", "members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/members", "sub_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/sub_teams", "open_membership_invitations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/open_membership_invitations", "proposed_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/proposed_members", "memberships_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/memberships_details", "invited_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/invited_members", "deactivated_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/deactivated_members", "irc_nicknames_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/irc_nicknames", "is_valid": false, "latitude": null, "confirmed_email_addresses_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/confirmed_email_addresses", "mailing_list_auto_subscribe_policy": "Ask me when I join a team", "team_owner_link": null, "members_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/members_details", "hide_email_addresses": true, "admins_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/admins", "visibility": "Public", "self_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater", "preferred_email_address_link": null, "date_created": "2006-05-23T12:49:30.483464+00:00", "display_name": "Bug Watch Updater", "expired_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/expired_members", "homepage_content": null, "name": "bug-watch-updater", "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#person", "super_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/super_teams", "participants_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/participants", "time_zone": null, "longitude": null, "mugshot_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/mugshot", "is_team": false, "karma": 0, "wiki_names_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/wiki_names", "participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/participations", "jabber_ids_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/jabber_ids", "indirect_participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-watch-updater\/indirect_participations"}, {"languages_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/languages", "members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/members", "sub_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/sub_teams", "open_membership_invitations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/open_membership_invitations", "proposed_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/proposed_members", "memberships_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/memberships_details", "invited_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/invited_members", "deactivated_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/deactivated_members", "irc_nicknames_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/irc_nicknames", "is_valid": true, "latitude": null, "confirmed_email_addresses_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/confirmed_email_addresses", "mailing_list_auto_subscribe_policy": "Ask me when I join a team", "team_owner_link": null, "members_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/members_details", "hide_email_addresses": false, "admins_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/admins", "visibility": "Public", "self_link": "http:\/\/api.launchpad.dev\/beta\/~carlos", "preferred_email_address_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/+email\/carlos@canonical.com", "date_created": "2005-06-06T08:59:51.615543+00:00", "display_name": "Carlos Perell\u00f3 Mar\u00edn", "expired_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/expired_members", "homepage_content": null, "name": "carlos", "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#person", "super_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/super_teams", "participants_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/participants", "time_zone": null, "longitude": null, "mugshot_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/mugshot", "is_team": false, "karma": 9, "wiki_names_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/wiki_names", "participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/participations", "jabber_ids_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/jabber_ids", "indirect_participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~carlos\/indirect_participations"}, {"languages_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/languages", "members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/members", "sub_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/sub_teams", "open_membership_invitations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/open_membership_invitations", "proposed_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/proposed_members", "memberships_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/memberships_details", "invited_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/invited_members", "deactivated_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/deactivated_members", "irc_nicknames_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/irc_nicknames", "is_valid": false, "latitude": null, "confirmed_email_addresses_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/confirmed_email_addresses", "mailing_list_auto_subscribe_policy": "Ask me when I join a team", "team_owner_link": null, "members_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/members_details", "hide_email_addresses": false, "admins_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/admins", "visibility": "Public", "self_link": "http:\/\/api.launchpad.dev\/beta\/~valyag", "preferred_email_address_link": null, "date_created": "2005-06-06T08:59:51.562857+00:00", "display_name": "Carlos Valdivia Yag\u00fce", "expired_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/expired_members", "homepage_content": null, "name": "valyag", "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#person", "super_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/super_teams", "participants_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/participants", "time_zone": null, "longitude": null, "mugshot_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/mugshot", "is_team": false, "karma": 0, "wiki_names_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/wiki_names", "participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/participations", "jabber_ids_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/jabber_ids", "indirect_participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~valyag\/indirect_participations"}, {"languages_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/languages", "members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/members", "sub_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/sub_teams", "open_membership_invitations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/open_membership_invitations", "proposed_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/proposed_members", "memberships_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/memberships_details", "invited_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/invited_members", "deactivated_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/deactivated_members", "irc_nicknames_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/irc_nicknames", "is_valid": true, "latitude": null, "confirmed_email_addresses_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/confirmed_email_addresses", "mailing_list_auto_subscribe_policy": "Ask me when I join a team", "team_owner_link": null, "members_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/members_details", "hide_email_addresses": false, "admins_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/admins", "visibility": "Public", "self_link": "http:\/\/api.launchpad.dev\/beta\/~cprov", "preferred_email_address_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/+email\/celso.providelo@canonical.com", "date_created": "2005-06-06T08:59:51.597050+00:00", "display_name": "Celso Providelo", "expired_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/expired_members", "homepage_content": null, "name": "cprov", "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#person", "super_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/super_teams", "participants_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/participants", "time_zone": null, "longitude": null, "mugshot_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/mugshot", "is_team": false, "karma": 0, "wiki_names_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/wiki_names", "participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/participations", "jabber_ids_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/jabber_ids", "indirect_participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~cprov\/indirect_participations"}, {"languages_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/languages", "members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/members", "sub_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/sub_teams", "open_membership_invitations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/open_membership_invitations", "proposed_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/proposed_members", "memberships_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/memberships_details", "invited_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/invited_members", "deactivated_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/deactivated_members", "irc_nicknames_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/irc_nicknames", "is_valid": false, "latitude": null, "confirmed_email_addresses_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/confirmed_email_addresses", "mailing_list_auto_subscribe_policy": "Ask me when I join a team", "team_owner_link": null, "members_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/members_details", "hide_email_addresses": false, "admins_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/admins", "visibility": "Public", "self_link": "http:\/\/api.launchpad.dev\/beta\/~kiko", "preferred_email_address_link": null, "date_created": "2005-06-06T08:59:51.594941+00:00", "display_name": "Christian Reis", "expired_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/expired_members", "homepage_content": null, "name": "kiko", "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#person", "super_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/super_teams", "participants_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/participants", "time_zone": null, "longitude": null, "mugshot_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/mugshot", "is_team": false, "karma": 0, "wiki_names_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/wiki_names", "participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/participations", "jabber_ids_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/jabber_ids", "indirect_participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~kiko\/indirect_participations"}], "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#people"} wadllib-1.3.2/src/wadllib/tests/data/launchpad-wadl.xml 0000664 0001750 0001750 00000554515 12112731513 023316 0 ustar barry barry 0000000 0000000
The root of the web service.
The name of the operation being invoked.
The name of the operation being invoked.
<strong>Search text</strong>
The name of the operation being invoked.
Display Name: This team's name as you would like it displayed throughout Launchpad.
Name: A short unique name, beginning with a lower-case letter or number, and containing only letters, numbers, dots, hyphens, or plus signs.
Subscription period: Number of days a new subscription lasts before expiring. You can customize the length of an individual subscription when approving it. Leave this empty or set to 0 for subscriptions to never expire.
Team Description: Use plain text; URLs will be linkified
Renewal period: Number of days a subscription lasts after being renewed. You can customize the lengths of individual renewals, but this is what's used for auto-renewed and user-renewed memberships.
<strong>Subscription policy</strong>
The name of the operation being invoked.
The name of the operation being invoked.
The name of the operation being invoked.
<strong>Search text</strong>
The name of the operation being invoked.
<strong>Search text</strong>
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>Owner</strong>
<strong>Nickname</strong>
<strong>IRC network</strong>
<strong>Nickname</strong>
<strong>IRC network</strong>
The canonical link to this resource.
The link to the WADL description of this resource.
Name: At least one lowercase letter or number, followed by letters, dots, hyphens or plusses. Keep this name short, as it is used in URLs.
Part of: Super-project. In Launchpad, we can setup a special "project group" that is an overarching initiative that includes several related projects. For example, the Mozilla Project produces Firefox, Thunderbird and Gecko. This information is used to group those projects in a coherent way. If you make this project part of a group, the group preferences and decisions around bug tracking, translation and security policy will apply to this project.
Name: At least one lowercase letter or number, followed by letters, dots, hyphens or plusses. Keep this name short, as it is used in URLs.
Part of: Super-project. In Launchpad, we can setup a special "project group" that is an overarching initiative that includes several related projects. For example, the Mozilla Project produces Firefox, Thunderbird and Gecko. This information is used to group those projects in a coherent way. If you make this project part of a group, the group preferences and decisions around bug tracking, translation and security policy will apply to this project.
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>Wiki host</strong>
<strong>Owner</strong>
<strong>The URL for this wiki home page.</strong>
<strong>Wikiname</strong>
<strong>Wiki host</strong>
<strong>Wikiname</strong>
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>The state of this membership</strong>
<strong>Member</strong>
<strong>Team</strong>
<strong>The state of this membership</strong>
<strong>Team</strong>
<strong>Team</strong>
<strong>Team</strong>
<strong>Team</strong>
<strong>The latitude of this object.</strong>
<strong>The time zone of this object.</strong>
<strong>The longitude of this object.</strong>
<strong>Team</strong>
The canonical link to this resource.
The link to the WADL description of this resource.
Mugshot: A large image of exactly 192x192 pixels, that will be displayed on the team page in Launchpad. It should be no bigger than 100kb in size.
<strong>List of Jabber IDs of this Person.</strong>
<strong>All WikiNames of this Person.</strong>
All subteams of this team.:
A subteam is any team that is a member (either directly or
indirectly) of this team. As an example, let's say we have
this hierarchy of teams:
Rosetta Translators
Rosetta pt Translators
Rosetta pt_BR Translators
In this case, both 'Rosetta pt Translators' and 'Rosetta pt_BR
Translators' are subteams of the 'Rosetta Translators' team,
and all members of both subteams are considered members of
"Rosetta Translators".
<strong>When someone's membership is about to expire, notify them and</strong>
<strong>All teams in which this person is an indirect member.</strong>
<strong>The longitude of this object.</strong>
Subscription policy: 'Moderated' means all subscriptions must be approved. 'Open' means any user can join without approval. 'Restricted' means new members can be added only by a team administrator.
<strong>All teams in which this person is a participant.</strong>
<strong>Is this object a team?</strong>
Preferred email address: The preferred email address for this person. The one we'll use to communicate with them.
Open membership invitations.: All TeamMemberships which represent an invitation (to join a team) sent to this person.
Confirmed e-mails of this person.: Confirmed e-mails are the ones in the VALIDATED state
<strong>List of languages known by this person</strong>
<strong>Hide my email addresses from other Launchpad users</strong>
All participants of this team.: List of all direct and indirect people and teams who, one way or another, are a part of this team. If you want a method to check if a given person is a member of a team, you should probably look at IPerson.inTeam().
<strong>This is an active user or a team.</strong>
All superteams of this team.:
A superteam is any team that this team is a member of. For
example, let's say we have this hierarchy of teams, and we are
the "Rosetta pt_BR Translators":
Rosetta Translators
Rosetta pt Translators
Rosetta pt_BR Translators
In this case, we will return both 'Rosetta pt Translators' and
'Rosetta Translators', because we are member of both of them.
<strong>The latitude of this object.</strong>
<strong>All `ITeamMembership`s for Teams this Person is an active member of.</strong>
<strong>Team Owner</strong>
Mailing List Auto-subscription Policy: This attribute determines whether a person is automatically subscribed to a team's mailing list when the person joins said team.
Subscription period: Number of days a new subscription lasts before expiring. You can customize the length of an individual subscription when approving it. Leave this empty or set to 0 for subscriptions to never expire.
<strong>All members whose membership is in the EXPIRED state</strong>
Active `ITeamMembership`s for this object's members.: Active TeamMemberships are the ones with the ADMIN or APPROVED status. The results are ordered using Person.sortingColumns.
Visibility: Public visibility is standard, and Private Membership means that a team's members are hidden.
Renewal period: Number of days a subscription lasts after being renewed. You can customize the lengths of individual renewals, but this is what's used for auto-renewed and user-renewed memberships.
<strong>List of members with ADMIN or APPROVED status</strong>
<strong>Date Created</strong>
<strong>All members whose membership is in the INVITED state</strong>
Display Name: This team's name as you would like it displayed throughout Launchpad.
Team Description: Use plain text; URLs will be linkified
Name: A short unique name, beginning with a lower-case letter or number, and containing only letters, numbers, dots, hyphens, or plus signs.
Time zone: The time zone of where you live.
<strong>All members whose membership is in the PROPOSED state</strong>
<strong>List of this team's admins.</strong>
<strong>List of IRC nicknames of this Person.</strong>
Karma: The cached total karma for this person.
<strong>All members whose membership is in the DEACTIVATED state</strong>
Homepage Content: The content of your home page. Edit this and it will be displayed for all the world to see.
Mugshot: A large image of exactly 192x192 pixels, that will be displayed on the team page in Launchpad. It should be no bigger than 100kb in size.
<strong>When someone's membership is about to expire, notify them and</strong>
Subscription policy: 'Moderated' means all subscriptions must be approved. 'Open' means any user can join without approval. 'Restricted' means new members can be added only by a team administrator.
<strong>Hide my email addresses from other Launchpad users</strong>
<strong>Team Owner</strong>
Mailing List Auto-subscription Policy: This attribute determines whether a person is automatically subscribed to a team's mailing list when the person joins said team.
Subscription period: Number of days a new subscription lasts before expiring. You can customize the length of an individual subscription when approving it. Leave this empty or set to 0 for subscriptions to never expire.
Visibility: Public visibility is standard, and Private Membership means that a team's members are hidden.
Renewal period: Number of days a subscription lasts after being renewed. You can customize the lengths of individual renewals, but this is what's used for auto-renewed and user-renewed memberships.
Display Name: This team's name as you would like it displayed throughout Launchpad.
Team Description: Use plain text; URLs will be linkified
Name: A short unique name, beginning with a lower-case letter or number, and containing only letters, numbers, dots, hyphens, or plus signs.
Time zone: The time zone of where you live.
Homepage Content: The content of your home page. Edit this and it will be displayed for all the world to see.
The canonical link to this resource.
The link to the WADL description of this resource.
Name: A unique name, used in URLs, identifying the project
group. All lowercase, no special characters.
Examples: apache, mozilla, gimp.
Name: A unique name, used in URLs, identifying the project
group. All lowercase, no special characters.
Examples: apache, mozilla, gimp.
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>The attachment content.</strong>
<strong>The message that was created when we added this attachment.</strong>
Attachment Type: The type of the attachment, for example Patch or Unspecified.
<strong>The bug the attachment belongs to.</strong>
Title: A short and descriptive description of the attachment
<strong>The attachment content.</strong>
<strong>The message that was created when we added this attachment.</strong>
Attachment Type: The type of the attachment, for example Patch or Unspecified.
<strong>The bug the attachment belongs to.</strong>
Title: A short and descriptive description of the attachment
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>Person</strong>
<strong>Email Address</strong>
The canonical link to this resource.
The link to the WADL description of this resource.
Contact details: The contact details for the external bug tracker (so that, for example, its administrators can be contacted about a security breach).
Name: An URL-friendly name for the bug tracker, such as "mozilla-bugs".
<strong>Bug Tracker Type</strong>
Title: A descriptive label for this tracker to show in listings.
Location: The top-level URL for the bug tracker, or an upstream email address. This must be accurate so that Launchpad can link to external bug reports.
<strong>The remote watches on this bug tracker.</strong>
Summary: A brief introduction or overview of this bug tracker instance.
Location aliases: A list of URLs or email addresses that all lead to the same bug tracker, or commonly seen typos, separated by whitespace.
<strong>Owner</strong>
Contact details: The contact details for the external bug tracker (so that, for example, its administrators can be contacted about a security breach).
Name: An URL-friendly name for the bug tracker, such as "mozilla-bugs".
<strong>Bug Tracker Type</strong>
Title: A descriptive label for this tracker to show in listings.
Location: The top-level URL for the bug tracker, or an upstream email address. This must be accurate so that Launchpad can link to external bug reports.
Summary: A brief introduction or overview of this bug tracker instance.
Location aliases: A list of URLs or email addresses that all lead to the same bug tracker, or commonly seen typos, separated by whitespace.
<strong>Owner</strong>
<strong>The state of this membership</strong>
<strong>Member</strong>
<strong>Team</strong>
<strong>The state of this membership</strong>
<strong>Team</strong>
<strong>Team</strong>
<strong>Team</strong>
<strong>Team</strong>
<strong>The latitude of this object.</strong>
<strong>The time zone of this object.</strong>
<strong>The longitude of this object.</strong>
<strong>Team</strong>
The canonical link to this resource.
The link to the WADL description of this resource.
Mugshot: A large image of exactly 192x192 pixels, that will be displayed on your home page in Launchpad. Traditionally this is a great big picture of your grinning face. Make the most of it! It should be no bigger than 100kb in size.
<strong>List of Jabber IDs of this Person.</strong>
<strong>All WikiNames of this Person.</strong>
All subteams of this team.:
A subteam is any team that is a member (either directly or
indirectly) of this team. As an example, let's say we have
this hierarchy of teams:
Rosetta Translators
Rosetta pt Translators
Rosetta pt_BR Translators
In this case, both 'Rosetta pt Translators' and 'Rosetta pt_BR
Translators' are subteams of the 'Rosetta Translators' team,
and all members of both subteams are considered members of
"Rosetta Translators".
<strong>All teams in which this person is an indirect member.</strong>
<strong>The longitude of this object.</strong>
<strong>All teams in which this person is a participant.</strong>
<strong>Is this object a team?</strong>
Preferred email address: The preferred email address for this person. The one we'll use to communicate with them.
Open membership invitations.: All TeamMemberships which represent an invitation (to join a team) sent to this person.
Confirmed e-mails of this person.: Confirmed e-mails are the ones in the VALIDATED state
<strong>List of languages known by this person</strong>
<strong>Hide my email addresses from other Launchpad users</strong>
All participants of this team.: List of all direct and indirect people and teams who, one way or another, are a part of this team. If you want a method to check if a given person is a member of a team, you should probably look at IPerson.inTeam().
<strong>This is an active user or a team.</strong>
All superteams of this team.:
A superteam is any team that this team is a member of. For
example, let's say we have this hierarchy of teams, and we are
the "Rosetta pt_BR Translators":
Rosetta Translators
Rosetta pt Translators
Rosetta pt_BR Translators
In this case, we will return both 'Rosetta pt Translators' and
'Rosetta Translators', because we are member of both of them.
<strong>The latitude of this object.</strong>
<strong>All `ITeamMembership`s for Teams this Person is an active member of.</strong>
<strong>Team Owner</strong>
Mailing List Auto-subscription Policy: This attribute determines whether a person is automatically subscribed to a team's mailing list when the person joins said team.
<strong>All members whose membership is in the EXPIRED state</strong>
Active `ITeamMembership`s for this object's members.: Active TeamMemberships are the ones with the ADMIN or APPROVED status. The results are ordered using Person.sortingColumns.
Visibility: Public visibility is standard, and Private Membership means that a team's members are hidden.
<strong>List of members with ADMIN or APPROVED status</strong>
<strong>Date Created</strong>
<strong>All members whose membership is in the INVITED state</strong>
Display Name: Your name as you would like it displayed throughout Launchpad. Most people use their full name here.
Name: A short unique name, beginning with a lower-case letter or number, and containing only letters, numbers, dots, hyphens, or plus signs.
Time zone: The time zone of where you live.
<strong>All members whose membership is in the PROPOSED state</strong>
<strong>List of this team's admins.</strong>
<strong>List of IRC nicknames of this Person.</strong>
Karma: The cached total karma for this person.
<strong>All members whose membership is in the DEACTIVATED state</strong>
Homepage Content: The content of your home page. Edit this and it will be displayed for all the world to see.
Mugshot: A large image of exactly 192x192 pixels, that will be displayed on your home page in Launchpad. Traditionally this is a great big picture of your grinning face. Make the most of it! It should be no bigger than 100kb in size.
<strong>Hide my email addresses from other Launchpad users</strong>
<strong>Team Owner</strong>
Mailing List Auto-subscription Policy: This attribute determines whether a person is automatically subscribed to a team's mailing list when the person joins said team.
Visibility: Public visibility is standard, and Private Membership means that a team's members are hidden.
Display Name: Your name as you would like it displayed throughout Launchpad. Most people use their full name here.
Name: A short unique name, beginning with a lower-case letter or number, and containing only letters, numbers, dots, hyphens, or plus signs.
Time zone: The time zone of where you live.
Homepage Content: The content of your home page. Edit this and it will be displayed for all the world to see.
The canonical link to this resource.
The link to the WADL description of this resource.
Person: The person's Launchpad ID or e-mail address. You can only subscribe someone who has a Launchpad account.
<strong>Bug</strong>
Subscribed by: The person who created this subscription.
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>Owner</strong>
<strong>Jabber user ID</strong>
<strong>Jabber user ID</strong>
<strong>Person</strong>
Bug System: You can register new bug trackers from the Launchpad Bugs home page.
Remote Bug: The bug number of this bug in the remote bug tracker.
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>List of bug attachments.</strong>
<strong>MultiJoin of the bugs which are dups of this one</strong>
<strong>Date Last Updated</strong>
This bug report should be private: Private bug reports are visible only to their subscribers.
<strong>Date Made Private</strong>
Who Made Private: The person who set this bug private.
<strong>The owner's IPerson</strong>
<strong>BugTasks on this bug, sorted upstream, then ubuntu, then other distroseriess.</strong>
<strong>Bug ID</strong>
<strong>Duplicate Of</strong>
Summary: A one-line summary of the problem.
Tags: Separated by whitespace.
<strong>This bug is a security vulnerability</strong>
Description: A detailed description of the problem,
including the steps required to reproduce it.
<strong>Subscriptions.</strong>
Does the bug's state permit expiration?: Expiration is permitted when the bug is not valid anywhere, a message was sent to the bug reporter, and the bug is associated with pillars that have enabled bug expiration.
Nickname: A short and unique name.
Add one only if you often need to retype the URL
but have trouble remembering the bug number.
<strong>The messages related to this object, in reverse order of creation (so newest first).</strong>
<strong>All bug watches associated with this bug.</strong>
<strong>Date Created</strong>
<strong>Can the Incomplete bug expire if it becomes inactive? Expiration may happen when the bug permits expiration, and a bugtask cannot be confirmed.</strong>
True or False depending on whether this bug is considered completely addressed. A bug is Launchpad is completely addressed when there are no tasks that are still open for the bug.
<strong>Date of last bug message</strong>
This bug report should be private: Private bug reports are visible only to their subscribers.
<strong>The owner's IPerson</strong>
<strong>Duplicate Of</strong>
Summary: A one-line summary of the problem.
Tags: Separated by whitespace.
<strong>This bug is a security vulnerability</strong>
Description: A detailed description of the problem,
including the steps required to reproduce it.
Nickname: A short and unique name.
Add one only if you often need to retype the URL
but have trouble remembering the bug number.
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>Release Series</strong>
Version: The specific version number assigned to this release. Letters and numbers are acceptable, for releases like "1.2rc3".
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>Parent</strong>
<strong>All the text/plain chunks joined together as a unicode string.</strong>
<strong>A list of BugAttachments connected to this message.</strong>
<strong>Person</strong>
<strong>Date Created</strong>
<strong>Subject</strong>
<strong>All the text/plain chunks joined together as a unicode string.</strong>
The canonical link to this resource.
The link to the WADL description of this resource.
Name: Only letters, numbers, and simple punctuation are allowed.
<strong>The product or distribution of this milestone.</strong>
Name: Only letters, numbers, and simple punctuation are allowed.
<strong>The product or distribution of this milestone.</strong>
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>Importance</strong>
<strong>Status</strong>
<strong>Assigned to</strong>
The canonical link to this resource.
The link to the WADL description of this resource.
Date Closed: The date on which this task was marked either Fix Committed or Fix Released.
<strong>Status</strong>
<strong>The target as presented in mail notifications</strong>
Date Confirmed: The date on which this task was marked Confirmed.
IBugTasks related to this one, namely other IBugTasks on the same IBug.
<strong>Importance</strong>
Date left new: The date on which this task was marked with a status higher than New.
<strong>The title of the bug related to this bugtask</strong>
Date Triaged: The date on which this task was marked Triaged.
Remote Bug Details: Select the bug watch that represents this task in the relevant bug tracker. If none of the bug watches represents this particular bug task, leave it as (None). Linking the remote bug watch with the task in this way means that a change in the remote bug status will change the status of this bug task in Launchpad.
<strong>Assigned to</strong>
True or False depending on whether or not there is more work required on this bug task.
Date Fix Committed: The date on which this task was marked Fix Committed.
<strong>The short, descriptive name of the target</strong>
Date Fix Relesaed: The date on which this task was marked Fix Released.
<strong>The owner</strong>
Date Created: The date on which this task was created.
Date In Progress: The date on which this task was marked In Progress.
<strong>Bug</strong>
Date Assigned: The date on which this task was assigned to someone.
Remote Bug Details: Select the bug watch that represents this task in the relevant bug tracker. If none of the bug watches represents this particular bug task, leave it as (None). Linking the remote bug watch with the task in this way means that a change in the remote bug status will change the status of this bug task in Launchpad.
<strong>The owner</strong>
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>Remote Status</strong>
<strong>Remote Importance</strong>
<strong>Bug watch title</strong>
<strong>The URL at which to view the remote bug.</strong>
<strong>Last Error Type</strong>
Remote Bug: The bug number of this bug in the remote bug tracker.
The tasks which this watch will affect. In Launchpad, a bug watch can be linked to one or more tasks, and if it is linked and we notice a status change in the watched bug then we will try to update the Launchpad bug task accordingly.
Bug System: You can register new bug trackers from the Launchpad Bugs home page.
<strong>Last Checked</strong>
<strong>Owner</strong>
<strong>Date Created</strong>
<strong>Bug</strong>
<strong>Last Changed</strong>
<strong>Remote Status</strong>
<strong>Remote Importance</strong>
<strong>Last Error Type</strong>
Remote Bug: The bug number of this bug in the remote bug tracker.
Bug System: You can register new bug trackers from the Launchpad Bugs home page.
<strong>Last Checked</strong>
<strong>Last Changed</strong>
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>Project</strong>
Name: The name of the series is a short, unique name that identifies it, being used in URLs. It must be all lowercase, with no special characters. For example, '2.0' or 'trunk'.
<strong>Project</strong>
Name: The name of the series is a short, unique name that identifies it, being used in URLs. It must be all lowercase, with no special characters. For example, '2.0' or 'trunk'.
The canonical link to this resource.
The link to the WADL description of this resource.
<strong>The state of this membership</strong>
<strong>Comment on the last change</strong>
<strong>Member</strong>
<strong>Date expires</strong>
<strong>Last person who change this</strong>
<strong>Team</strong>
Date joined: The date in which this membership was made active for the first time.
<strong>Member</strong>
<strong>Date expires</strong>
<strong>Last person who change this</strong>
<strong>Team</strong>
wadllib-1.3.2/src/wadllib/tests/data/person-limi.json 0000664 0001750 0001750 00000004532 12112731513 023026 0 ustar barry barry 0000000 0000000 {"languages_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/languages", "members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/members", "sub_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/sub_teams", "open_membership_invitations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/open_membership_invitations", "proposed_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/proposed_members", "memberships_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/memberships_details", "invited_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/invited_members", "deactivated_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/deactivated_members", "irc_nicknames_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/irc_nicknames", "is_valid": false, "latitude": null, "confirmed_email_addresses_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/confirmed_email_addresses", "mailing_list_auto_subscribe_policy": "Ask me when I join a team", "team_owner_link": null, "members_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/members_details", "hide_email_addresses": false, "admins_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/admins", "visibility": "Public", "self_link": "http:\/\/api.launchpad.dev\/beta\/~limi", "preferred_email_address_link": null, "date_created": "2005-06-06T08:59:51.619713+00:00", "display_name": "Alexander Limi", "expired_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/expired_members", "homepage_content": null, "name": "limi", "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#person", "super_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/super_teams", "participants_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/participants", "time_zone": null, "longitude": null, "mugshot_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/mugshot", "is_team": false, "karma": 0, "wiki_names_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/wiki_names", "participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/participations", "jabber_ids_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/jabber_ids", "indirect_participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/indirect_participations"} wadllib-1.3.2/src/wadllib/tests/data/__init__.py 0000664 0001750 0001750 00000000000 12112731513 021770 0 ustar barry barry 0000000 0000000 wadllib-1.3.2/src/wadllib/tests/data/multipart-binary-wadl.xml 0000664 0001750 0001750 00000002163 12112731513 024645 0 ustar barry barry 0000000 0000000
wadllib-1.3.2/src/wadllib/tests/data/options-wadl.xml 0000664 0001750 0001750 00000001572 12112731513 023040 0 ustar barry barry 0000000 0000000
wadllib-1.3.2/src/wadllib/tests/data/root.json 0000664 0001750 0001750 00000000210 12112731513 021540 0 ustar barry barry 0000000 0000000 {"people_collection_link": "http:\/\/api.launchpad.dev\/beta\/people", "bugs_collection_link": "http:\/\/api.launchpad.dev\/beta\/bugs"} wadllib-1.3.2/src/wadllib/tests/data/data-types-wadl.xml 0000664 0001750 0001750 00000001650 12112731513 023415 0 ustar barry barry 0000000 0000000
wadllib-1.3.2/src/wadllib/tests/data/personset.json 0000664 0001750 0001750 00000031337 12112731513 022615 0 ustar barry barry 0000000 0000000 {"total_size": 63, "start": 0, "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#people", "next_collection_link": "http:\/\/api.launchpad.dev\/beta\/people?ws.start=5&ws.size=5", "entries": [{"languages_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/languages", "members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/members", "sub_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/sub_teams", "open_membership_invitations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/open_membership_invitations", "proposed_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/proposed_members", "memberships_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/memberships_details", "invited_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/invited_members", "deactivated_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/deactivated_members", "irc_nicknames_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/irc_nicknames", "is_valid": false, "latitude": null, "confirmed_email_addresses_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/confirmed_email_addresses", "mailing_list_auto_subscribe_policy": "Ask me when I join a team", "team_owner_link": null, "members_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/members_details", "hide_email_addresses": false, "admins_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/admins", "visibility": "Public", "self_link": "http:\/\/api.launchpad.dev\/beta\/~limi", "preferred_email_address_link": null, "date_created": "2005-06-06T08:59:51.619713+00:00", "display_name": "Alexander Limi", "expired_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/expired_members", "homepage_content": null, "name": "limi", "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#person", "super_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/super_teams", "participants_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/participants", "time_zone": null, "longitude": null, "mugshot_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/mugshot", "is_team": false, "karma": 0, "wiki_names_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/wiki_names", "participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/participations", "jabber_ids_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/jabber_ids", "indirect_participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~limi\/indirect_participations"}, {"languages_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/languages", "members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/members", "sub_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/sub_teams", "open_membership_invitations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/open_membership_invitations", "proposed_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/proposed_members", "memberships_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/memberships_details", "invited_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/invited_members", "deactivated_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/deactivated_members", "irc_nicknames_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/irc_nicknames", "is_valid": false, "latitude": null, "confirmed_email_addresses_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/confirmed_email_addresses", "mailing_list_auto_subscribe_policy": "Ask me when I join a team", "team_owner_link": null, "members_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/members_details", "hide_email_addresses": false, "admins_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/admins", "visibility": "Public", "self_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez", "preferred_email_address_link": null, "date_created": "2005-06-06T08:59:51.559519+00:00", "display_name": "Aloriel", "expired_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/expired_members", "homepage_content": null, "name": "jorge-gonzalez-gonzalez", "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#person", "super_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/super_teams", "participants_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/participants", "time_zone": null, "longitude": null, "mugshot_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/mugshot", "is_team": false, "karma": 0, "wiki_names_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/wiki_names", "participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/participations", "jabber_ids_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/jabber_ids", "indirect_participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~jorge-gonzalez-gonzalez\/indirect_participations"}, {"languages_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/languages", "members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/members", "sub_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/sub_teams", "open_membership_invitations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/open_membership_invitations", "proposed_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/proposed_members", "memberships_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/memberships_details", "invited_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/invited_members", "deactivated_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/deactivated_members", "irc_nicknames_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/irc_nicknames", "is_valid": false, "latitude": null, "confirmed_email_addresses_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/confirmed_email_addresses", "mailing_list_auto_subscribe_policy": "Ask me when I join a team", "team_owner_link": null, "members_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/members_details", "hide_email_addresses": false, "admins_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/admins", "visibility": "Public", "self_link": "http:\/\/api.launchpad.dev\/beta\/~spiv", "preferred_email_address_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/+email\/andrew.bennetts@ubuntulinux.com", "date_created": "2005-06-06T08:59:51.551196+00:00", "display_name": "Andrew Bennetts", "expired_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/expired_members", "homepage_content": null, "name": "spiv", "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#person", "super_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/super_teams", "participants_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/participants", "time_zone": null, "longitude": null, "mugshot_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/mugshot", "is_team": false, "karma": 0, "wiki_names_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/wiki_names", "participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/participations", "jabber_ids_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/jabber_ids", "indirect_participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~spiv\/indirect_participations"}, {"languages_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/languages", "members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/members", "sub_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/sub_teams", "open_membership_invitations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/open_membership_invitations", "proposed_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/proposed_members", "memberships_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/memberships_details", "invited_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/invited_members", "deactivated_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/deactivated_members", "irc_nicknames_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/irc_nicknames", "is_valid": false, "latitude": null, "confirmed_email_addresses_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/confirmed_email_addresses", "mailing_list_auto_subscribe_policy": "Ask me when I join a team", "team_owner_link": null, "members_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/members_details", "hide_email_addresses": false, "admins_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/admins", "visibility": "Public", "self_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop", "preferred_email_address_link": null, "date_created": "2005-06-06T08:59:51.561685+00:00", "display_name": "Andr\u00e9 Lu\u00eds Lopes", "expired_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/expired_members", "homepage_content": null, "name": "andrelop", "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#person", "super_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/super_teams", "participants_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/participants", "time_zone": null, "longitude": null, "mugshot_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/mugshot", "is_team": false, "karma": 0, "wiki_names_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/wiki_names", "participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/participations", "jabber_ids_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/jabber_ids", "indirect_participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~andrelop\/indirect_participations"}, {"languages_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/languages", "members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/members", "sub_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/sub_teams", "open_membership_invitations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/open_membership_invitations", "proposed_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/proposed_members", "memberships_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/memberships_details", "invited_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/invited_members", "deactivated_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/deactivated_members", "irc_nicknames_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/irc_nicknames", "is_valid": true, "latitude": null, "confirmed_email_addresses_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/confirmed_email_addresses", "mailing_list_auto_subscribe_policy": "Ask me when I join a team", "team_owner_link": null, "members_details_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/members_details", "hide_email_addresses": false, "admins_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/admins", "visibility": "Public", "self_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer", "preferred_email_address_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/+email\/bug-importer@launchpad.net", "date_created": "2005-12-06T09:48:58.287679+00:00", "display_name": "Bug Importer", "expired_members_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/expired_members", "homepage_content": null, "name": "bug-importer", "resource_type_link": "http:\/\/api.launchpad.dev\/beta\/#person", "super_teams_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/super_teams", "participants_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/participants", "time_zone": null, "longitude": null, "mugshot_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/mugshot", "is_team": false, "karma": 0, "wiki_names_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/wiki_names", "participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/participations", "jabber_ids_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/jabber_ids", "indirect_participations_collection_link": "http:\/\/api.launchpad.dev\/beta\/~bug-importer\/indirect_participations"}]} wadllib-1.3.2/src/wadllib.egg-info/ 0000775 0001750 0001750 00000000000 12112756624 017322 5 ustar barry barry 0000000 0000000 wadllib-1.3.2/src/wadllib.egg-info/SOURCES.txt 0000664 0001750 0001750 00000001565 12112756624 021215 0 ustar barry barry 0000000 0000000 COPYING.txt
HACKING.txt
MANIFEST.in
README.txt
ez_setup.py
setup.py
src/wadllib/NEWS.txt
src/wadllib/README.txt
src/wadllib/__init__.py
src/wadllib/application.py
src/wadllib/iso_strptime.py
src/wadllib/version.txt
src/wadllib.egg-info/PKG-INFO
src/wadllib.egg-info/SOURCES.txt
src/wadllib.egg-info/dependency_links.txt
src/wadllib.egg-info/not-zip-safe
src/wadllib.egg-info/requires.txt
src/wadllib.egg-info/top_level.txt
src/wadllib/tests/__init__.py
src/wadllib/tests/test_docs.py
src/wadllib/tests/data/__init__.py
src/wadllib/tests/data/data-types-wadl.xml
src/wadllib/tests/data/launchpad-wadl.xml
src/wadllib/tests/data/links-wadl.xml
src/wadllib/tests/data/multipart-binary-wadl.xml
src/wadllib/tests/data/options-wadl.xml
src/wadllib/tests/data/person-limi.json
src/wadllib/tests/data/personset-page2.json
src/wadllib/tests/data/personset.json
src/wadllib/tests/data/root.json wadllib-1.3.2/src/wadllib.egg-info/requires.txt 0000664 0001750 0001750 00000000067 12112756624 021725 0 ustar barry barry 0000000 0000000 setuptools
lazr.uri
[docs]
Sphinx
z3c.recipe.sphinxdoc wadllib-1.3.2/src/wadllib.egg-info/top_level.txt 0000664 0001750 0001750 00000000010 12112756624 022043 0 ustar barry barry 0000000 0000000 wadllib
wadllib-1.3.2/src/wadllib.egg-info/dependency_links.txt 0000664 0001750 0001750 00000000001 12112756624 023370 0 ustar barry barry 0000000 0000000
wadllib-1.3.2/src/wadllib.egg-info/not-zip-safe 0000664 0001750 0001750 00000000001 12112755734 021551 0 ustar barry barry 0000000 0000000
wadllib-1.3.2/src/wadllib.egg-info/PKG-INFO 0000664 0001750 0001750 00000101237 12112756624 020423 0 ustar barry barry 0000000 0000000 Metadata-Version: 1.1
Name: wadllib
Version: 1.3.2
Summary: Navigate HTTP resources using WADL files as guides.
Home-page: https://launchpad.net/wadllib
Author: LAZR Developers
Author-email: lazr-developers@lists.launchpad.net
License: LGPL v3
Download-URL: https://launchpad.net/wadllib/+download
Description: ..
Copyright (C) 2008-2013 Canonical Ltd.
This file is part of wadllib.
wadllib is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, version 3 of the License.
wadllib is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details.
You should have received a copy of the GNU Lesser General Public License
along with wadllib. If not, see .
wadllib
*******
An Application object represents a web service described by a WADL
file.
>>> import os
>>> import sys
>>> import pkg_resources
>>> from wadllib.application import Application
The first argument to the Application constructor is the URL at which
the WADL file was found. The second argument may be raw WADL markup.
>>> wadl_string = pkg_resources.resource_string(
... 'wadllib.tests.data', 'launchpad-wadl.xml')
>>> wadl = Application("http://api.launchpad.dev/beta/", wadl_string)
Or the second argument may be an open filehandle containing the markup.
>>> 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/")
Link navigation
===============
The preferred technique for finding a resource is to start at one of
the resources defined in the WADL file, and follow links. This code
retrieves the definition of the root resource.
>>> service_root = wadl.get_resource_by_path('')
>>> service_root.url
'http://api.launchpad.dev/beta/'
>>> service_root.type_url
'#service-root'
The service root resource supports GET.
>>> get_method = service_root.get_method('get')
>>> get_method.id
'service-root-get'
>>> get_method = service_root.get_method('GET')
>>> get_method.id
'service-root-get'
If we want to invoke this method, we send a GET request to the service
root URL.
>>> get_method.name
'get'
>>> get_method.build_request_url()
'http://api.launchpad.dev/beta/'
The WADL description of a resource knows which representations are
available for that resource. In this case, the server root resource
has a a JSON representation, and it defines parameters like
'people_collection_link', a link to a list of people in Launchpad. We
should be able to use the get_parameter() method to get the WADL
definition of the 'people_collection_link' parameter and find out more
about it--for instance, is it a link to another resource?
>>> def test_raises(exc_class, method, *args, **kwargs):
... try:
... method(*args, **kwargs)
... except Exception:
... # Contortion to support Python < 2.6 and >= 3 simultaneously.
... e = sys.exc_info()[1]
... if isinstance(e, exc_class):
... print(e)
... return
... raise
... raise Exception("Expected exception %s not raised" % exc_class)
>>> from wadllib.application import NoBoundRepresentationError
>>> link_name = 'people_collection_link'
>>> test_raises(
... NoBoundRepresentationError, service_root.get_parameter, link_name)
Resource is not bound to any representation, and no media media type was specified.
Oops. The code has no way to know whether 'people_collection_link' is
a parameter of the JSON representation or some other kind of
representation. We can pass a media type to get_parameter and let it
know which representation the parameter lives in.
>>> link_parameter = service_root.get_parameter(
... link_name, 'application/json')
>>> test_raises(NoBoundRepresentationError, link_parameter.get_value)
Resource is not bound to any representation.
Oops again. The parameter is available, but it has no value, because
there's no actual data associated with the resource. The browser can
look up the description of the GET method to make an actual GET
request to the service root, and bind the resulting representation to
the WADL description of the service root.
You can't bind just any representation to a WADL resource description.
It has to be of a media type understood by the WADL description.
>>> from wadllib.application import UnsupportedMediaTypeError
>>> test_raises(
... UnsupportedMediaTypeError, service_root.bind,
... 'Some HTML', 'text/html')
This resource doesn't define a representation for media type text/html
The WADL description of the service root resource has a JSON
representation. Here it is.
>>> json_representation = service_root.get_representation_definition(
... 'application/json')
>>> json_representation.media_type
'application/json'
We already have a WADL representation of the service root resource, so
let's try binding it to that JSON representation. We use test JSON
data from a file to simulate the result of a GET request to the
service root.
>>> def get_testdata(filename):
... return pkg_resources.resource_string(
... 'wadllib.tests.data', filename + '.json')
>>> def bind_to_testdata(resource, filename):
... return resource.bind(get_testdata(filename), 'application/json')
The return value is a new Resource object that's "bound" to that JSON
test data.
>>> bound_service_root = bind_to_testdata(service_root, 'root')
>>> sorted([param.name for param in bound_service_root.parameters()])
['bugs_collection_link', 'people_collection_link']
>>> sorted(bound_service_root.parameter_names())
['bugs_collection_link', 'people_collection_link']
>>> [method.id for method in bound_service_root.method_iter]
['service-root-get']
Now the bound resource object has a JSON representation, and now
'people_collection_link' makes sense. We can follow the
'people_collection_link' to a new Resource object.
>>> link_parameter = bound_service_root.get_parameter(link_name)
>>> link_parameter.style
'plain'
>>> print(link_parameter.get_value())
http://api.launchpad.dev/beta/people
>>> personset_resource = link_parameter.linked_resource
>>> personset_resource.__class__
>>> print(personset_resource.url)
http://api.launchpad.dev/beta/people
>>> personset_resource.type_url
'http://api.launchpad.dev/beta/#people'
This new resource is a collection of people.
>>> personset_resource.id
'people'
The "collection of people" resource supports a standard GET request as
well as a special GET and an overloaded POST. The get_method() method
is used to retrieve WADL definitions of the possible HTTP requests you
might make. Here's how to get the WADL definition of the standard GET
request.
>>> get_method = personset_resource.get_method('get')
>>> get_method.id
'people-get'
The method name passed into get_method() is treated case-insensitively.
>>> personset_resource.get_method('GET').id
'people-get'
To invoke the special GET request, the client sets the 'ws.op' query
parameter to the fixed string 'findPerson'.
>>> find_method = personset_resource.get_method(
... query_params={'ws.op' : 'findPerson'})
>>> find_method.id
'people-findPerson'
Given an end-user's values for the non-fixed parameters, it's possible
to get the URL that should be used to invoke the method.
>>> print(find_method.build_request_url(text='foo'))
http://api.launchpad.dev/beta/people?text=foo&ws.op=findPerson
>>> print(find_method.build_request_url(
... {'ws.op' : 'findPerson', 'text' : 'bar'}))
http://api.launchpad.dev/beta/people?text=bar&ws.op=findPerson
An error occurs if the end-user gives an incorrect value for a fixed
parameter value, or omits a required parameter.
>>> find_method.build_request_url()
Traceback (most recent call last):
...
ValueError: No value for required parameter 'text'
>>> find_method.build_request_url(
... {'ws.op' : 'findAPerson', 'text' : 'foo'})
... # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
Traceback (most recent call last):
...
ValueError: Value 'findAPerson' for parameter 'ws.op' conflicts
with fixed value 'findPerson'
To invoke the overloaded POST request, the client sets the 'ws.op'
query variable to the fixed string 'newTeam':
>>> create_team_method = personset_resource.get_method(
... 'post', representation_params={'ws.op' : 'newTeam'})
>>> create_team_method.id
'people-newTeam'
findMethod() returns None when there's no WADL method matching the
name or the fixed parameters.
>>> print(personset_resource.get_method('nosuchmethod'))
None
>>> print(personset_resource.get_method(
... 'post', query_params={'ws_op' : 'nosuchparam'}))
None
Let's say the browser makes a GET request to the person set resource
and gets back a representation. We can bind that representation to our
description of the person set resource.
>>> bound_personset = bind_to_testdata(personset_resource, 'personset')
>>> bound_personset.get_parameter("start").get_value()
0
>>> bound_personset.get_parameter("total_size").get_value()
63
We can keep following links indefinitely, so long as we bind to a
representation to each resource as we get it, and use the
representation to find the next link.
>>> next_page_link = bound_personset.get_parameter("next_collection_link")
>>> print(next_page_link.get_value())
http://api.launchpad.dev/beta/people?ws.start=5&ws.size=5
>>> page_two = next_page_link.linked_resource
>>> bound_page_two = bind_to_testdata(page_two, 'personset-page2')
>>> print(bound_page_two.url)
http://api.launchpad.dev/beta/people?ws.start=5&ws.size=5
>>> bound_page_two.get_parameter("start").get_value()
5
>>> print(bound_page_two.get_parameter("next_collection_link").get_value())
http://api.launchpad.dev/beta/people?ws.start=10&ws.size=5
Let's say the browser makes a POST request that invokes the 'newTeam'
named operation. The response will include a number of HTTP headers,
including 'Location', which points the way to the newly created team.
>>> headers = { 'Location' : 'http://api.launchpad.dev/~newteam' }
>>> response = create_team_method.response.bind(headers)
>>> location_parameter = response.get_parameter('Location')
>>> location_parameter.get_value()
'http://api.launchpad.dev/~newteam'
>>> new_team = location_parameter.linked_resource
>>> new_team.url
'http://api.launchpad.dev/~newteam'
>>> new_team.type_url
'http://api.launchpad.dev/beta/#team'
Examining links
---------------
The 'linked_resource' property of a parameter lets you follow a link
to another object. The 'link' property of a parameter lets you examine
links before following them.
>>> try:
... import simplejson as json
... except ImportError:
... import json
>>> links_wadl = application_for('links-wadl.xml')
>>> service_root = links_wadl.get_resource_by_path('')
>>> representation = json.dumps(
... {'scalar_value': 'foo',
... 'known_link': 'http://known/',
... 'unknown_link': 'http://unknown/'})
>>> bound_root = service_root.bind(representation)
>>> print(bound_root.get_parameter("scalar_value").link)
None
>>> known_resource = bound_root.get_parameter("known_link")
>>> unknown_resource = bound_root.get_parameter("unknown_link")
>>> print(known_resource.link.can_follow)
True
>>> print(unknown_resource.link.can_follow)
False
A link whose type is unknown is a link to a resource not described by
WADL. Following this link using .linked_resource or .link.follow will
cause a wadllib error. You'll need to follow the link using a general
HTTP library or some other tool.
>>> known_resource.link.follow
>>> known_resource.linked_resource
>>> from wadllib.application import WADLError
>>> test_raises(WADLError, getattr, unknown_resource.link, 'follow')
Cannot follow a link when the target has no WADL
description. Try using a general HTTP client instead.
>>> test_raises(WADLError, getattr, unknown_resource, 'linked_resource')
Cannot follow a link when the target has no WADL
description. Try using a general HTTP client instead.
Creating a Resource from a representation definition
====================================================
Although every representation is a representation of some HTTP
resource, an HTTP resource doesn't necessarily correspond directly to
a WADL or tag. Sometimes a representation
is defined within a WADL tag.
>>> 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'))
There may be no WADL or tag for the
representation defined here. That's why wadllib makes it possible to
instantiate an anonymous Resource object using only the representation
definition.
>>> from wadllib.application import Resource
>>> anonymous_resource = Resource(
... wadl, "http://foo/", representation_definition.tag)
We can bind this resource to a representation, as long as we
explicitly pass in the representation definition.
>>> anonymous_resource = anonymous_resource.bind(
... get_testdata('personset'), 'application/json',
... representation_definition=representation_definition)
Once the resource is bound to a representation, we can get its
parameter values.
>>> print(anonymous_resource.get_parameter(
... 'total_size', 'application/json').get_value())
63
Resource instantiation
======================
If you happen to have the URL to an object lying around, and you know
its type, you can construct a Resource object directly instead of
by following links.
>>> 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
You can bind a Resource to a representation when you create it.
>>> 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
By default the representation is treated as a string and processed
according to the media type you pass into the Resource constructor. If
you've already processed the representation, pass in False for the
'representation_needs_processing' argument.
>>> 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
Most of the time, the representation of a resource is of the type
you'd get by sending a standard GET to that resource. If that's not
the case, you can specify a RepresentationDefinition as the
'representation_definition' argument to bind() or the Resource
constructor, to show what the representation really looks like. Here's
an example.
There's a method on a person resource such as bound_limi that's
identified by a distinctive query argument: ws.op=getMembersByStatus.
>>> method = bound_limi.get_method(
... query_params={'ws.op' : 'findPathToTeam'})
Invoke this method with a GET request and you'll get back a page from
a list of people.
>>> 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'
As it happens, we have a page from a list of people to use as test data.
>>> people_page_repr = get_testdata('personset')
If we bind the resource to the result of the method invocation as
happened above, we don't be able to access any of the parameters we'd
expect. wadllib will think the representation is of type
'person-full', the default GET type for bound_limi.
>>> bad_people_page = bound_limi.bind(people_page_repr)
>>> print(bad_people_page.get_parameter('total_size'))
None
Since we don't actually have a 'person-full' representation, we won't
be able to get values for the parameters of that kind of
representation.
>>> bad_people_page.get_parameter('name').get_value()
Traceback (most recent call last):
...
KeyError: 'name'
So that's a dead end. *But*, if we pass the correct representation
type into bind(), we can access the parameters associated with a
'person-page' representation.
>>> people_page = bound_limi.bind(
... people_page_repr,
... representation_definition=people_page_repr_definition)
>>> people_page.get_parameter('total_size').get_value()
63
If you invoke the method and ask for a media type other than JSON, you
won't get anything.
>>> print(method.response.get_representation_definition('text/html'))
None
Data type conversion
--------------------
The values of date and dateTime parameters are automatically converted to
Python datetime objects.
>>> 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, ...)
A 'date' field can include a timestamp, and a 'datetime' field can
omit one. wadllib will turn both into datetime objects.
>>> 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, ...)
If a date or dateTime parameter has a null value, you get None. If the
value is a string that can't be parsed to a datetime object, you get a
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 creation
=======================
You must provide a representation when invoking certain methods. The
representation() method helps you build one without knowing the
details of how a representation is put together.
>>> 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')
The return value of build_representation is a 2-tuple containing the
media type of the built representation, and the string representation
itself. Along with the resource's URL, this is all you need to send
the representation to a web server.
>>> bound_limi.get_method('patch').build_representation(name='limi2')
('application/json', '{"name": "limi2"}')
Representations may require values for certain parameters.
>>> 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'
Some representations may safely include binary data.
>>> 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('')
>>> method = service_root.get_method('post', 'multipart/form-data')
>>> media_type, doc = method.build_representation(
... text_field="text", binary_field="\x01\x02")
>>> print(media_type)
multipart/form-data; boundary=...
>>> print(doc)
MIME-Version: 1.0
Content-Type: multipart/form-data; boundary="..."
--...
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Disposition: form-data; name="text_field"
text
--...
Content-Type: application/octet-stream
MIME-Version: 1.0
Content-Disposition: form-data; name="binary_field"
...
--...
>>> '\x01\x02' in doc
True
>>> method = service_root.get_method('post', 'text/unknown')
>>> method.build_representation(field="value")
Traceback (most recent call last):
...
ValueError: Unsupported media type: 'text/unknown'
Options
=======
Some parameters take values from a predefined list of options.
>>> 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']
Such parameters cannot take values that are not in the list.
>>> 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"
Error conditions
================
You'll get None if you try to look up a nonexistent resource.
>>> print(wadl.get_resource_by_path('nosuchresource'))
None
You'll get an exception if you try to look up a nonexistent resource
type.
>>> print(wadl.get_resource_type('#nosuchtype'))
Traceback (most recent call last):
KeyError: 'No such XML ID: "#nosuchtype"'
You'll get None if you try to look up a method whose parameters don't
match any defined method.
>>> print(bound_limi.get_method(
... 'post', representation_params={ 'foo' : 'bar' }))
None
.. cleanup
>>> for stream in cleanups:
... stream.close()
.. toctree::
:glob:
*
docs/*
================
NEWS for wadllib
================
1.3.2 (2013-02-25)
==================
- Impose sort order to avoid test failures due to hash randomization.
LP: #1132125
- Be sure to close streams opened by pkg_resources.resource_stream() to avoid
test suite complaints.
1.3.1 (2012-03-22)
==================
- Correct the double pass through _from_string causing datetime issues
1.3.0 (2012-01-27)
==================
- Add Python 3 compatibility
- Add the ability to inspect links before following them.
- Ensure that the sample data is packaged.
1.2.0 (2011-02-03)
==================
- It's now possible to examine a link before following it, to see
whether it has a WADL description or whether it needs to be fetched
with a general HTTP client.
- It's now possible to iterate over a resource's Parameter objects
with the .parameters() method.
1.1.8 (2010-10-27)
==================
- This revision contains no code changes, but the build system was
changed (yet again). This time to include the version.txt file
used by setup.py.
1.1.7 (2010-10-26)
==================
- This revision contains no code changes, but the build system was
changed (again) to include the sample data used in tests.
1.1.6 (2010-10-21)
==================
- This revision contains no code changes, but the build system was
changed to include the sample data used in tests.
1.1.5 (2010-05-04)
==================
- Fixed a bug (Launchpad bug 274074) that prevented the lookup of
parameter values in resources associated directly with a
representation definition (rather than a resource type with a
representation definition). Bug fix provided by James Westby.
1.1.4 (2009-09-15)
==================
- Fixed a bug that crashed wadllib unless all parameters of a
multipart representation were provided.
1.1.3 (2009-08-26)
==================
- Remove unnecessary build dependencies.
- Add missing dependencies to setup file.
- Remove sys.path hack from setup.py.
1.1.2 (2009-08-20)
==================
- Consistently handle different versions of simplejson.
1.1.1 (2009-07-14)
==================
- Make wadllib aware of the tags that go beneath tags.
1.1 (2009-07-09)
================
- Make wadllib capable of recognizing and generating
multipart/form-data representations, including representations that
incorporate binary parameters.
1.0 (2009-03-23)
================
- Initial release on PyPI
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
wadllib-1.3.2/PKG-INFO 0000664 0001750 0001750 00000101237 12112756624 014524 0 ustar barry barry 0000000 0000000 Metadata-Version: 1.1
Name: wadllib
Version: 1.3.2
Summary: Navigate HTTP resources using WADL files as guides.
Home-page: https://launchpad.net/wadllib
Author: LAZR Developers
Author-email: lazr-developers@lists.launchpad.net
License: LGPL v3
Download-URL: https://launchpad.net/wadllib/+download
Description: ..
Copyright (C) 2008-2013 Canonical Ltd.
This file is part of wadllib.
wadllib is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the
Free Software Foundation, version 3 of the License.
wadllib is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
more details.
You should have received a copy of the GNU Lesser General Public License
along with wadllib. If not, see .
wadllib
*******
An Application object represents a web service described by a WADL
file.
>>> import os
>>> import sys
>>> import pkg_resources
>>> from wadllib.application import Application
The first argument to the Application constructor is the URL at which
the WADL file was found. The second argument may be raw WADL markup.
>>> wadl_string = pkg_resources.resource_string(
... 'wadllib.tests.data', 'launchpad-wadl.xml')
>>> wadl = Application("http://api.launchpad.dev/beta/", wadl_string)
Or the second argument may be an open filehandle containing the markup.
>>> 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/")
Link navigation
===============
The preferred technique for finding a resource is to start at one of
the resources defined in the WADL file, and follow links. This code
retrieves the definition of the root resource.
>>> service_root = wadl.get_resource_by_path('')
>>> service_root.url
'http://api.launchpad.dev/beta/'
>>> service_root.type_url
'#service-root'
The service root resource supports GET.
>>> get_method = service_root.get_method('get')
>>> get_method.id
'service-root-get'
>>> get_method = service_root.get_method('GET')
>>> get_method.id
'service-root-get'
If we want to invoke this method, we send a GET request to the service
root URL.
>>> get_method.name
'get'
>>> get_method.build_request_url()
'http://api.launchpad.dev/beta/'
The WADL description of a resource knows which representations are
available for that resource. In this case, the server root resource
has a a JSON representation, and it defines parameters like
'people_collection_link', a link to a list of people in Launchpad. We
should be able to use the get_parameter() method to get the WADL
definition of the 'people_collection_link' parameter and find out more
about it--for instance, is it a link to another resource?
>>> def test_raises(exc_class, method, *args, **kwargs):
... try:
... method(*args, **kwargs)
... except Exception:
... # Contortion to support Python < 2.6 and >= 3 simultaneously.
... e = sys.exc_info()[1]
... if isinstance(e, exc_class):
... print(e)
... return
... raise
... raise Exception("Expected exception %s not raised" % exc_class)
>>> from wadllib.application import NoBoundRepresentationError
>>> link_name = 'people_collection_link'
>>> test_raises(
... NoBoundRepresentationError, service_root.get_parameter, link_name)
Resource is not bound to any representation, and no media media type was specified.
Oops. The code has no way to know whether 'people_collection_link' is
a parameter of the JSON representation or some other kind of
representation. We can pass a media type to get_parameter and let it
know which representation the parameter lives in.
>>> link_parameter = service_root.get_parameter(
... link_name, 'application/json')
>>> test_raises(NoBoundRepresentationError, link_parameter.get_value)
Resource is not bound to any representation.
Oops again. The parameter is available, but it has no value, because
there's no actual data associated with the resource. The browser can
look up the description of the GET method to make an actual GET
request to the service root, and bind the resulting representation to
the WADL description of the service root.
You can't bind just any representation to a WADL resource description.
It has to be of a media type understood by the WADL description.
>>> from wadllib.application import UnsupportedMediaTypeError
>>> test_raises(
... UnsupportedMediaTypeError, service_root.bind,
... 'Some HTML', 'text/html')
This resource doesn't define a representation for media type text/html
The WADL description of the service root resource has a JSON
representation. Here it is.
>>> json_representation = service_root.get_representation_definition(
... 'application/json')
>>> json_representation.media_type
'application/json'
We already have a WADL representation of the service root resource, so
let's try binding it to that JSON representation. We use test JSON
data from a file to simulate the result of a GET request to the
service root.
>>> def get_testdata(filename):
... return pkg_resources.resource_string(
... 'wadllib.tests.data', filename + '.json')
>>> def bind_to_testdata(resource, filename):
... return resource.bind(get_testdata(filename), 'application/json')
The return value is a new Resource object that's "bound" to that JSON
test data.
>>> bound_service_root = bind_to_testdata(service_root, 'root')
>>> sorted([param.name for param in bound_service_root.parameters()])
['bugs_collection_link', 'people_collection_link']
>>> sorted(bound_service_root.parameter_names())
['bugs_collection_link', 'people_collection_link']
>>> [method.id for method in bound_service_root.method_iter]
['service-root-get']
Now the bound resource object has a JSON representation, and now
'people_collection_link' makes sense. We can follow the
'people_collection_link' to a new Resource object.
>>> link_parameter = bound_service_root.get_parameter(link_name)
>>> link_parameter.style
'plain'
>>> print(link_parameter.get_value())
http://api.launchpad.dev/beta/people
>>> personset_resource = link_parameter.linked_resource
>>> personset_resource.__class__
>>> print(personset_resource.url)
http://api.launchpad.dev/beta/people
>>> personset_resource.type_url
'http://api.launchpad.dev/beta/#people'
This new resource is a collection of people.
>>> personset_resource.id
'people'
The "collection of people" resource supports a standard GET request as
well as a special GET and an overloaded POST. The get_method() method
is used to retrieve WADL definitions of the possible HTTP requests you
might make. Here's how to get the WADL definition of the standard GET
request.
>>> get_method = personset_resource.get_method('get')
>>> get_method.id
'people-get'
The method name passed into get_method() is treated case-insensitively.
>>> personset_resource.get_method('GET').id
'people-get'
To invoke the special GET request, the client sets the 'ws.op' query
parameter to the fixed string 'findPerson'.
>>> find_method = personset_resource.get_method(
... query_params={'ws.op' : 'findPerson'})
>>> find_method.id
'people-findPerson'
Given an end-user's values for the non-fixed parameters, it's possible
to get the URL that should be used to invoke the method.
>>> print(find_method.build_request_url(text='foo'))
http://api.launchpad.dev/beta/people?text=foo&ws.op=findPerson
>>> print(find_method.build_request_url(
... {'ws.op' : 'findPerson', 'text' : 'bar'}))
http://api.launchpad.dev/beta/people?text=bar&ws.op=findPerson
An error occurs if the end-user gives an incorrect value for a fixed
parameter value, or omits a required parameter.
>>> find_method.build_request_url()
Traceback (most recent call last):
...
ValueError: No value for required parameter 'text'
>>> find_method.build_request_url(
... {'ws.op' : 'findAPerson', 'text' : 'foo'})
... # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
Traceback (most recent call last):
...
ValueError: Value 'findAPerson' for parameter 'ws.op' conflicts
with fixed value 'findPerson'
To invoke the overloaded POST request, the client sets the 'ws.op'
query variable to the fixed string 'newTeam':
>>> create_team_method = personset_resource.get_method(
... 'post', representation_params={'ws.op' : 'newTeam'})
>>> create_team_method.id
'people-newTeam'
findMethod() returns None when there's no WADL method matching the
name or the fixed parameters.
>>> print(personset_resource.get_method('nosuchmethod'))
None
>>> print(personset_resource.get_method(
... 'post', query_params={'ws_op' : 'nosuchparam'}))
None
Let's say the browser makes a GET request to the person set resource
and gets back a representation. We can bind that representation to our
description of the person set resource.
>>> bound_personset = bind_to_testdata(personset_resource, 'personset')
>>> bound_personset.get_parameter("start").get_value()
0
>>> bound_personset.get_parameter("total_size").get_value()
63
We can keep following links indefinitely, so long as we bind to a
representation to each resource as we get it, and use the
representation to find the next link.
>>> next_page_link = bound_personset.get_parameter("next_collection_link")
>>> print(next_page_link.get_value())
http://api.launchpad.dev/beta/people?ws.start=5&ws.size=5
>>> page_two = next_page_link.linked_resource
>>> bound_page_two = bind_to_testdata(page_two, 'personset-page2')
>>> print(bound_page_two.url)
http://api.launchpad.dev/beta/people?ws.start=5&ws.size=5
>>> bound_page_two.get_parameter("start").get_value()
5
>>> print(bound_page_two.get_parameter("next_collection_link").get_value())
http://api.launchpad.dev/beta/people?ws.start=10&ws.size=5
Let's say the browser makes a POST request that invokes the 'newTeam'
named operation. The response will include a number of HTTP headers,
including 'Location', which points the way to the newly created team.
>>> headers = { 'Location' : 'http://api.launchpad.dev/~newteam' }
>>> response = create_team_method.response.bind(headers)
>>> location_parameter = response.get_parameter('Location')
>>> location_parameter.get_value()
'http://api.launchpad.dev/~newteam'
>>> new_team = location_parameter.linked_resource
>>> new_team.url
'http://api.launchpad.dev/~newteam'
>>> new_team.type_url
'http://api.launchpad.dev/beta/#team'
Examining links
---------------
The 'linked_resource' property of a parameter lets you follow a link
to another object. The 'link' property of a parameter lets you examine
links before following them.
>>> try:
... import simplejson as json
... except ImportError:
... import json
>>> links_wadl = application_for('links-wadl.xml')
>>> service_root = links_wadl.get_resource_by_path('')
>>> representation = json.dumps(
... {'scalar_value': 'foo',
... 'known_link': 'http://known/',
... 'unknown_link': 'http://unknown/'})
>>> bound_root = service_root.bind(representation)
>>> print(bound_root.get_parameter("scalar_value").link)
None
>>> known_resource = bound_root.get_parameter("known_link")
>>> unknown_resource = bound_root.get_parameter("unknown_link")
>>> print(known_resource.link.can_follow)
True
>>> print(unknown_resource.link.can_follow)
False
A link whose type is unknown is a link to a resource not described by
WADL. Following this link using .linked_resource or .link.follow will
cause a wadllib error. You'll need to follow the link using a general
HTTP library or some other tool.
>>> known_resource.link.follow
>>> known_resource.linked_resource
>>> from wadllib.application import WADLError
>>> test_raises(WADLError, getattr, unknown_resource.link, 'follow')
Cannot follow a link when the target has no WADL
description. Try using a general HTTP client instead.
>>> test_raises(WADLError, getattr, unknown_resource, 'linked_resource')
Cannot follow a link when the target has no WADL
description. Try using a general HTTP client instead.
Creating a Resource from a representation definition
====================================================
Although every representation is a representation of some HTTP
resource, an HTTP resource doesn't necessarily correspond directly to
a WADL or tag. Sometimes a representation
is defined within a WADL tag.
>>> 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'))
There may be no WADL or tag for the
representation defined here. That's why wadllib makes it possible to
instantiate an anonymous Resource object using only the representation
definition.
>>> from wadllib.application import Resource
>>> anonymous_resource = Resource(
... wadl, "http://foo/", representation_definition.tag)
We can bind this resource to a representation, as long as we
explicitly pass in the representation definition.
>>> anonymous_resource = anonymous_resource.bind(
... get_testdata('personset'), 'application/json',
... representation_definition=representation_definition)
Once the resource is bound to a representation, we can get its
parameter values.
>>> print(anonymous_resource.get_parameter(
... 'total_size', 'application/json').get_value())
63
Resource instantiation
======================
If you happen to have the URL to an object lying around, and you know
its type, you can construct a Resource object directly instead of
by following links.
>>> 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
You can bind a Resource to a representation when you create it.
>>> 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
By default the representation is treated as a string and processed
according to the media type you pass into the Resource constructor. If
you've already processed the representation, pass in False for the
'representation_needs_processing' argument.
>>> 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
Most of the time, the representation of a resource is of the type
you'd get by sending a standard GET to that resource. If that's not
the case, you can specify a RepresentationDefinition as the
'representation_definition' argument to bind() or the Resource
constructor, to show what the representation really looks like. Here's
an example.
There's a method on a person resource such as bound_limi that's
identified by a distinctive query argument: ws.op=getMembersByStatus.
>>> method = bound_limi.get_method(
... query_params={'ws.op' : 'findPathToTeam'})
Invoke this method with a GET request and you'll get back a page from
a list of people.
>>> 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'
As it happens, we have a page from a list of people to use as test data.
>>> people_page_repr = get_testdata('personset')
If we bind the resource to the result of the method invocation as
happened above, we don't be able to access any of the parameters we'd
expect. wadllib will think the representation is of type
'person-full', the default GET type for bound_limi.
>>> bad_people_page = bound_limi.bind(people_page_repr)
>>> print(bad_people_page.get_parameter('total_size'))
None
Since we don't actually have a 'person-full' representation, we won't
be able to get values for the parameters of that kind of
representation.
>>> bad_people_page.get_parameter('name').get_value()
Traceback (most recent call last):
...
KeyError: 'name'
So that's a dead end. *But*, if we pass the correct representation
type into bind(), we can access the parameters associated with a
'person-page' representation.
>>> people_page = bound_limi.bind(
... people_page_repr,
... representation_definition=people_page_repr_definition)
>>> people_page.get_parameter('total_size').get_value()
63
If you invoke the method and ask for a media type other than JSON, you
won't get anything.
>>> print(method.response.get_representation_definition('text/html'))
None
Data type conversion
--------------------
The values of date and dateTime parameters are automatically converted to
Python datetime objects.
>>> 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, ...)
A 'date' field can include a timestamp, and a 'datetime' field can
omit one. wadllib will turn both into datetime objects.
>>> 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, ...)
If a date or dateTime parameter has a null value, you get None. If the
value is a string that can't be parsed to a datetime object, you get a
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 creation
=======================
You must provide a representation when invoking certain methods. The
representation() method helps you build one without knowing the
details of how a representation is put together.
>>> 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')
The return value of build_representation is a 2-tuple containing the
media type of the built representation, and the string representation
itself. Along with the resource's URL, this is all you need to send
the representation to a web server.
>>> bound_limi.get_method('patch').build_representation(name='limi2')
('application/json', '{"name": "limi2"}')
Representations may require values for certain parameters.
>>> 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'
Some representations may safely include binary data.
>>> 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('')
>>> method = service_root.get_method('post', 'multipart/form-data')
>>> media_type, doc = method.build_representation(
... text_field="text", binary_field="\x01\x02")
>>> print(media_type)
multipart/form-data; boundary=...
>>> print(doc)
MIME-Version: 1.0
Content-Type: multipart/form-data; boundary="..."
--...
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Disposition: form-data; name="text_field"
text
--...
Content-Type: application/octet-stream
MIME-Version: 1.0
Content-Disposition: form-data; name="binary_field"
...
--...
>>> '\x01\x02' in doc
True
>>> method = service_root.get_method('post', 'text/unknown')
>>> method.build_representation(field="value")
Traceback (most recent call last):
...
ValueError: Unsupported media type: 'text/unknown'
Options
=======
Some parameters take values from a predefined list of options.
>>> 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']
Such parameters cannot take values that are not in the list.
>>> 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"
Error conditions
================
You'll get None if you try to look up a nonexistent resource.
>>> print(wadl.get_resource_by_path('nosuchresource'))
None
You'll get an exception if you try to look up a nonexistent resource
type.
>>> print(wadl.get_resource_type('#nosuchtype'))
Traceback (most recent call last):
KeyError: 'No such XML ID: "#nosuchtype"'
You'll get None if you try to look up a method whose parameters don't
match any defined method.
>>> print(bound_limi.get_method(
... 'post', representation_params={ 'foo' : 'bar' }))
None
.. cleanup
>>> for stream in cleanups:
... stream.close()
.. toctree::
:glob:
*
docs/*
================
NEWS for wadllib
================
1.3.2 (2013-02-25)
==================
- Impose sort order to avoid test failures due to hash randomization.
LP: #1132125
- Be sure to close streams opened by pkg_resources.resource_stream() to avoid
test suite complaints.
1.3.1 (2012-03-22)
==================
- Correct the double pass through _from_string causing datetime issues
1.3.0 (2012-01-27)
==================
- Add Python 3 compatibility
- Add the ability to inspect links before following them.
- Ensure that the sample data is packaged.
1.2.0 (2011-02-03)
==================
- It's now possible to examine a link before following it, to see
whether it has a WADL description or whether it needs to be fetched
with a general HTTP client.
- It's now possible to iterate over a resource's Parameter objects
with the .parameters() method.
1.1.8 (2010-10-27)
==================
- This revision contains no code changes, but the build system was
changed (yet again). This time to include the version.txt file
used by setup.py.
1.1.7 (2010-10-26)
==================
- This revision contains no code changes, but the build system was
changed (again) to include the sample data used in tests.
1.1.6 (2010-10-21)
==================
- This revision contains no code changes, but the build system was
changed to include the sample data used in tests.
1.1.5 (2010-05-04)
==================
- Fixed a bug (Launchpad bug 274074) that prevented the lookup of
parameter values in resources associated directly with a
representation definition (rather than a resource type with a
representation definition). Bug fix provided by James Westby.
1.1.4 (2009-09-15)
==================
- Fixed a bug that crashed wadllib unless all parameters of a
multipart representation were provided.
1.1.3 (2009-08-26)
==================
- Remove unnecessary build dependencies.
- Add missing dependencies to setup file.
- Remove sys.path hack from setup.py.
1.1.2 (2009-08-20)
==================
- Consistently handle different versions of simplejson.
1.1.1 (2009-07-14)
==================
- Make wadllib aware of the tags that go beneath tags.
1.1 (2009-07-09)
================
- Make wadllib capable of recognizing and generating
multipart/form-data representations, including representations that
incorporate binary parameters.
1.0 (2009-03-23)
================
- Initial release on PyPI
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
wadllib-1.3.2/COPYING.txt 0000664 0001750 0001750 00000016727 12112731513 015277 0 ustar barry barry 0000000 0000000 GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
wadllib-1.3.2/ez_setup.py 0000664 0001750 0001750 00000022467 12112731513 015634 0 ustar barry barry 0000000 0000000 #!python
"""Bootstrap setuptools installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from ez_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import sys
DEFAULT_VERSION = "0.6c11"
DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
md5_data = {
'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
}
import sys, os
def _validate_md5(egg_name, data):
if egg_name in md5_data:
from md5 import md5
digest = md5(data).hexdigest()
if digest != md5_data[egg_name]:
print >>sys.stderr, (
"md5 validation of %s failed! (Possible download problem?)"
% egg_name
)
sys.exit(2)
return data
def use_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
download_delay=15, min_version=None
):
"""Automatically find/download setuptools and make it available on sys.path
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end with
a '/'). `to_dir` is the directory where setuptools will be downloaded, if
it is not already available. If `download_delay` is specified, it should
be the number of seconds that will be paused before initiating a download,
should one be required. If an older version of setuptools is installed,
this routine will print a message to ``sys.stderr`` and raise SystemExit in
an attempt to abort the calling script.
"""
# Work around a hack in the ez_setup.py file from simplejson==1.7.3.
if min_version:
version = min_version
was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
def do_download():
egg = download_setuptools(version, download_base, to_dir, download_delay)
sys.path.insert(0, egg)
import setuptools; setuptools.bootstrap_install_from = egg
try:
import pkg_resources
except ImportError:
return do_download()
try:
pkg_resources.require("setuptools>="+version); return
except pkg_resources.VersionConflict, e:
if was_imported:
print >>sys.stderr, (
"The required version of setuptools (>=%s) is not available, and\n"
"can't be installed while this script is running. Please install\n"
" a more recent version first, using 'easy_install -U setuptools'."
"\n\n(Currently using %r)"
) % (version, e.args[0])
sys.exit(2)
else:
del pkg_resources, sys.modules['pkg_resources'] # reload ok
return do_download()
except pkg_resources.DistributionNotFound:
return do_download()
def download_setuptools(
version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
delay = 15
):
"""Download setuptools from a specified location and return its filename
`version` should be a valid setuptools version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download attempt.
"""
import urllib2, shutil
egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
url = download_base + egg_name
saveto = os.path.join(to_dir, egg_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
from distutils import log
if delay:
log.warn("""
---------------------------------------------------------------------------
This script requires setuptools version %s to run (even to display
help). I will attempt to download it for you (from
%s), but
you may need to enable firewall access for this script first.
I will start the download in %d seconds.
(Note: if this machine does not have network access, please obtain the file
%s
and place it in this directory before rerunning this script.)
---------------------------------------------------------------------------""",
version, download_base, delay, url
); from time import sleep; sleep(delay)
log.warn("Downloading %s", url)
src = urllib2.urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = _validate_md5(egg_name, src.read())
dst = open(saveto,"wb"); dst.write(data)
finally:
if src: src.close()
if dst: dst.close()
return os.path.realpath(saveto)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
try:
import setuptools
except ImportError:
egg = None
try:
egg = download_setuptools(version, delay=0)
sys.path.insert(0,egg)
from setuptools.command.easy_install import main
return main(list(argv)+[egg]) # we're done here
finally:
if egg and os.path.exists(egg):
os.unlink(egg)
else:
if setuptools.__version__ == '0.0.1':
print >>sys.stderr, (
"You have an obsolete version of setuptools installed. Please\n"
"remove it from your system entirely before rerunning this script."
)
sys.exit(2)
req = "setuptools>="+version
import pkg_resources
try:
pkg_resources.require(req)
except pkg_resources.VersionConflict:
try:
from setuptools.command.easy_install import main
except ImportError:
from easy_install import main
main(list(argv)+[download_setuptools(delay=0)])
sys.exit(0) # try to force an exit
else:
if argv:
from setuptools.command.easy_install import main
main(argv)
else:
print "Setuptools version",version,"or greater has been installed."
print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
def update_md5(filenames):
"""Update our built-in md5 registry"""
import re
from md5 import md5
for name in filenames:
base = os.path.basename(name)
f = open(name,'rb')
md5_data[base] = md5(f.read()).hexdigest()
f.close()
data = [" %r: %r,\n" % it for it in md5_data.items()]
data.sort()
repl = "".join(data)
import inspect
srcfile = inspect.getsourcefile(sys.modules[__name__])
f = open(srcfile, 'rb'); src = f.read(); f.close()
match = re.search("\nmd5_data = {\n([^}]+)}", src)
if not match:
print >>sys.stderr, "Internal error!"
sys.exit(2)
src = src[:match.start(1)] + repl + src[match.end(1):]
f = open(srcfile,'w')
f.write(src)
f.close()
if __name__=='__main__':
if len(sys.argv)>2 and sys.argv[1]=='--md5update':
update_md5(sys.argv[2:])
else:
main(sys.argv[1:])