Colin Watson has proposed merging ~cjwatson/launchpad:services-webservice-future-imports into launchpad:master.
Commit message: Convert lp.services.webservice to preferred __future__ imports Requested reviews: Launchpad code reviewers (launchpad-reviewers) For more details, see: https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/391159 -- Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:services-webservice-future-imports into launchpad:master.
diff --git a/lib/lp/services/webservice/doc/webservice-marshallers.txt b/lib/lp/services/webservice/doc/webservice-marshallers.txt index 765bf45..17cfa94 100644 --- a/lib/lp/services/webservice/doc/webservice-marshallers.txt +++ b/lib/lp/services/webservice/doc/webservice-marshallers.txt @@ -84,8 +84,8 @@ This marshaller also appends '_link' to the representation name of this field, so that clients can know this is a link to another resource and not a random string. - >>> marshaller.representation_name - 'some_person_link' + >>> print(marshaller.representation_name) + some_person_link If you export a Choice that uses an SQLObjectVocabularyBase then you get an error, as you should be using a ReferenceChoice instead to diff --git a/lib/lp/services/webservice/stories/apidoc.txt b/lib/lp/services/webservice/stories/apidoc.txt index 176071c..f6b62da 100644 --- a/lib/lp/services/webservice/stories/apidoc.txt +++ b/lib/lp/services/webservice/stories/apidoc.txt @@ -5,7 +5,7 @@ http://launchpad.test/+apidoc. It contains summaries of the different web service versions, and links to version-specific documents. >>> browser.open('http://launchpad.test/+apidoc') - >>> print extract_text(browser.contents) + >>> print(extract_text(browser.contents)) Launchpad Web Service API ... Launchpad web service API documentation @@ -19,6 +19,6 @@ The documentation for a specific version is located at http://launchpad.test/+apidoc/{version}.html. >>> browser.open('http://launchpad.test/+apidoc/devel.html') - >>> print browser.title + >>> print(browser.title) About this service diff --git a/lib/lp/services/webservice/stories/conditional-write.txt b/lib/lp/services/webservice/stories/conditional-write.txt index 8b25b26..44502fd 100644 --- a/lib/lp/services/webservice/stories/conditional-write.txt +++ b/lib/lp/services/webservice/stories/conditional-write.txt @@ -17,8 +17,8 @@ Here's a bug: it has an ETag and values for fields like When we add a message to a bug, 'date_last_message' is changed as a side effect. - >>> print webservice.named_post( - ... url, 'newMessage', subject="subject", content="content") + >>> print(webservice.named_post( + ... url, 'newMessage', subject="subject", content="content")) HTTP/1.1 201 Created ... @@ -37,7 +37,7 @@ changed: A conditional GET request using the old ETag will fail, and the client will hear about the new value for 'date_last_message'. - >>> print webservice.get(url, headers={'If-None-Match' : old_etag}) + >>> print(webservice.get(url, headers={'If-None-Match' : old_etag})) HTTP/1.1 200 Ok ... @@ -70,7 +70,7 @@ conditional write will succeed. >>> import simplejson >>> data = simplejson.dumps({'title' : 'New title'}) >>> headers = {'If-Match': old_etag} - >>> print webservice.patch(url, 'application/json', data, headers=headers) + >>> print(webservice.patch(url, 'application/json', data, headers=headers)) HTTP/1.1 209 Content Returned ... @@ -93,7 +93,7 @@ A conditional write will fail when the write portion of the submitted ETag doesn't match, even if the read portion matches. >>> headers = {'If-Match': new_etag} - >>> print webservice.patch(url, 'application/json', data, headers=headers) + >>> print(webservice.patch(url, 'application/json', data, headers=headers)) HTTP/1.1 412 Precondition Failed ... @@ -114,7 +114,7 @@ modified by mod_compress as though it were the original ETag. >>> etag = webservice.get(url).jsonBody()['http_etag'] >>> headers = {'If-None-Match': etag} - >>> print webservice.get(url, headers=headers) + >>> print(webservice.get(url, headers=headers)) HTTP/1.1 304 Not Modified ... @@ -122,18 +122,18 @@ Some versions of mod_compress turn '"foo"' into '"foo"-gzip', and some versions turn it into '"foo-gzip"'. We treat all three forms the same. >>> headers = {'If-None-Match': etag + "-gzip"} - >>> print webservice.get(url, headers=headers) + >>> print(webservice.get(url, headers=headers)) HTTP/1.1 304 Not Modified ... >>> headers = {'If-None-Match': etag[:-1] + "-gzip" + etag[-1]} - >>> print webservice.get(url, headers=headers) + >>> print(webservice.get(url, headers=headers)) HTTP/1.1 304 Not Modified ... Any other modification to the ETag is treated as a distinct ETag. >>> headers = {'If-None-Match': etag + "-not-gzip"} - >>> print webservice.get(url, headers=headers) + >>> print(webservice.get(url, headers=headers)) HTTP/1.1 200 Ok ... diff --git a/lib/lp/services/webservice/stories/datamodel.txt b/lib/lp/services/webservice/stories/datamodel.txt index 796c545..f47d6c3 100644 --- a/lib/lp/services/webservice/stories/datamodel.txt +++ b/lib/lp/services/webservice/stories/datamodel.txt @@ -19,9 +19,9 @@ Normally, the total size of a collection is not served along with the collection; it's available by following the total_size_link. >>> collection = get_collection() - >>> print sorted(collection.keys()) + >>> print(sorted(collection.keys())) [u'entries', u'next_collection_link', u'start', u'total_size_link'] - >>> print webservice.get(collection['total_size_link']).jsonBody() + >>> print(webservice.get(collection['total_size_link']).jsonBody()) 9 If an entire collection fits on one page (making the size of the @@ -29,9 +29,9 @@ collection obvious), 'total_size' is served instead of 'total_size_link'. >>> collection = get_collection(size=100) - >>> print sorted(collection.keys()) + >>> print(sorted(collection.keys())) [u'entries', u'start', u'total_size'] - >>> print collection['total_size'] + >>> print(collection['total_size']) 9 If the last page of the collection is fetched (making the total size @@ -39,7 +39,7 @@ of the collection semi-obvious), 'total_size' is served instead of 'total_size_link'. >>> collection = get_collection(start=8) - >>> print sorted(collection.keys()) + >>> print(sorted(collection.keys())) [u'entries', u'prev_collection_link', u'start', u'total_size'] - >>> print collection['total_size'] + >>> print(collection['total_size']) 9 diff --git a/lib/lp/services/webservice/stories/launchpadlib.txt b/lib/lp/services/webservice/stories/launchpadlib.txt index 50c25b4..5a79f52 100644 --- a/lib/lp/services/webservice/stories/launchpadlib.txt +++ b/lib/lp/services/webservice/stories/launchpadlib.txt @@ -20,7 +20,7 @@ Launchpad object for a given user with a single call. >>> launchpad = launchpadlib_for( ... u'launchpadlib test', 'salgado', 'WRITE_PUBLIC') - >>> print launchpad.me.name + >>> print(launchpad.me.name) salgado # XXX leonardr 2010-03-31 bug=552732 @@ -35,7 +35,7 @@ Launchpad object for a given user with a single call. #>>> launchpad = launchpadlib_for( #... u'launchpadlib test', 'no-priv', 'READ_PRIVATE', 'firefox', #... version="devel") - #>>> print launchpad.projects['firefox'].name + #>>> print(launchpad.projects['firefox'].name) #firefox With launchpadlib_credentials_for() you can get a launchpadlib @@ -47,9 +47,9 @@ Credentials object. >>> credentials <launchpadlib.credentials.Credentials object ...> - >>> print credentials.consumer.key + >>> print(credentials.consumer.key) launchpadlib test - >>> print credentials.access_token + >>> print(credentials.access_token) oauth_token_secret=...&oauth_token=... This can be used to create your own Launchpad object. Note you cannot @@ -59,7 +59,7 @@ scheme which does not work in the test environment. >>> from launchpadlib.launchpad import Launchpad >>> launchpad = Launchpad( ... credentials, None, None, 'http://api.launchpad.test/') - >>> print launchpad.me.name + >>> print(launchpad.me.name) no-priv Anonymous access @@ -72,13 +72,13 @@ The Launchpad object for the anonymous user can be used to access public information. >>> apache_results = lp_anon.project_groups.search(text="Apache") - >>> print apache_results[0].name + >>> print(apache_results[0].name) apache But trying to access information that requires a logged in user results in an error. - >>> print lp_anon.me.name + >>> print(lp_anon.me.name) Traceback (most recent call last): ... Unauthorized: HTTP Error 401: Unauthorized... diff --git a/lib/lp/services/webservice/stories/multiversion.txt b/lib/lp/services/webservice/stories/multiversion.txt index 7f90181..bc4f855 100644 --- a/lib/lp/services/webservice/stories/multiversion.txt +++ b/lib/lp/services/webservice/stories/multiversion.txt @@ -17,18 +17,18 @@ total size of the collection. ... return collection.jsonBody() >>> collection = get_collection("devel") - >>> print sorted(collection.keys()) + >>> print(sorted(collection.keys())) [u'entries', u'next_collection_link', u'start', u'total_size_link'] - >>> print webservice.get(collection['total_size_link']).jsonBody() + >>> print(webservice.get(collection['total_size_link']).jsonBody()) 9 In previous versions, the same named operations will return a 'total_size' containing the actual size of the collection. >>> collection = get_collection("1.0") - >>> print sorted(collection.keys()) + >>> print(sorted(collection.keys())) [u'entries', u'next_collection_link', u'start', u'total_size'] - >>> print collection['total_size'] + >>> print(collection['total_size']) 9 Mutator operations @@ -49,17 +49,17 @@ subsequent versions, those named operations are not published. Here's the 'beta' version, where the named operation works. >>> url = get_bugtask_path('beta') - >>> print webservice.named_post( + >>> print(webservice.named_post( ... url, 'transitionToImportance', importance='Low', - ... api_version='beta') + ... api_version='beta')) HTTP/1.1 200 Ok ... Now let's try the same thing in the '1.0' version, where it fails. >>> url = get_bugtask_path('1.0') - >>> print webservice.named_post( + >>> print(webservice.named_post( ... url, 'transitionToImportance', importance='Low', - ... api_version='devel') + ... api_version='devel')) HTTP/1.1 405 Method Not Allowed ... diff --git a/lib/lp/services/webservice/stories/root.txt b/lib/lp/services/webservice/stories/root.txt index 7ba36bc..aebe90f 100644 --- a/lib/lp/services/webservice/stories/root.txt +++ b/lib/lp/services/webservice/stories/root.txt @@ -5,7 +5,7 @@ special entry: the user account of the authenticated user. To avoid confusion when one program runs as different users, this is implemented as a redirect to that person's canonical URL. - >>> print webservice.get('/people/+me') + >>> print(webservice.get('/people/+me')) HTTP/1.1 303 See Other ... Location: http://.../~salgado diff --git a/lib/lp/services/webservice/stories/security.txt b/lib/lp/services/webservice/stories/security.txt index fa31fe2..77c09ea 100644 --- a/lib/lp/services/webservice/stories/security.txt +++ b/lib/lp/services/webservice/stories/security.txt @@ -21,7 +21,7 @@ Jokosher project. But the 'no-priv' user can't see bug number 14, which is private. - >>> print user_webservice.get("/bugs/14") + >>> print(user_webservice.get("/bugs/14")) HTTP/1.1 404 Not Found ... @@ -35,7 +35,7 @@ Things are a little different for a user who has permission to see private data, but is using an OAuth key that restricts the client to operating on public data. - >>> print public_webservice.get("/bugs/14") + >>> print(public_webservice.get("/bugs/14")) HTTP/1.1 404 Not Found ... diff --git a/lib/lp/services/webservice/stories/xx-hostedfile.txt b/lib/lp/services/webservice/stories/xx-hostedfile.txt index e792c62..bda79d6 100644 --- a/lib/lp/services/webservice/stories/xx-hostedfile.txt +++ b/lib/lp/services/webservice/stories/xx-hostedfile.txt @@ -19,13 +19,13 @@ Firefox starts out with a link to branding images, but no actual images. >>> project['brand_link'] u'http://.../firefox/brand' - >>> print webservice.get(project['icon_link']) + >>> print(webservice.get(project['icon_link'])) HTTP/1.1 404 Not Found ... - >>> print webservice.get(project['logo_link']) + >>> print(webservice.get(project['logo_link'])) HTTP/1.1 404 Not Found ... - >>> print webservice.get(project['brand_link']) + >>> print(webservice.get(project['brand_link'])) HTTP/1.1 404 Not Found ... @@ -39,16 +39,16 @@ We can upload branding images with PUT. ... 'images', filename) ... return open(image_file).read() - >>> print webservice.put(project['icon_link'], 'image/png', - ... load_image('team.png')) + >>> print(webservice.put(project['icon_link'], 'image/png', + ... load_image('team.png'))) HTTP/1.1 200 Ok ... - >>> print webservice.put(project['logo_link'], 'image/png', - ... load_image('team-logo.png')) + >>> print(webservice.put(project['logo_link'], 'image/png', + ... load_image('team-logo.png'))) HTTP/1.1 200 Ok ... - >>> print webservice.put(project['brand_link'], 'image/png', - ... load_image('team-mugshot.png')) + >>> print(webservice.put(project['brand_link'], 'image/png', + ... load_image('team-mugshot.png'))) HTTP/1.1 200 Ok ... @@ -56,21 +56,21 @@ The project's branding links now redirects you to files maintained by the librarian. >>> result = webservice.get(project['icon_link']) - >>> print result + >>> print(result) HTTP/1.1 303 See Other ... Location: http://.../icon ... >>> result = webservice.get(project['logo_link']) - >>> print result + >>> print(result) HTTP/1.1 303 See Other ... Location: http://.../logo ... >>> result = webservice.get(project['brand_link']) - >>> print result + >>> print(result) HTTP/1.1 303 See Other ... Location: http://.../brand @@ -83,8 +83,8 @@ Error handling Launchpad's ImageUpload classes enforce restrictions on uploaded images. You can't upload an image that's the wrong type. - >>> print webservice.put( - ... project['brand_link'], 'image/png', 'Not an image') + >>> print(webservice.put( + ... project['brand_link'], 'image/png', 'Not an image')) HTTP/1.1 400 Bad Request ... The file uploaded was not recognized as an image; please check it @@ -92,8 +92,8 @@ images. You can't upload an image that's the wrong type. You also can't upload an image that's the wrong size. - >>> print webservice.put(project['brand_link'], 'image/png', - ... load_image('team-logo.png')) + >>> print(webservice.put(project['brand_link'], 'image/png', + ... load_image('team-logo.png'))) HTTP/1.1 400 Bad Request ... This image is not exactly 192x192 pixels in size. diff --git a/lib/lp/services/webservice/stories/xx-service.txt b/lib/lp/services/webservice/stories/xx-service.txt index 24c7931..92e31f5 100644 --- a/lib/lp/services/webservice/stories/xx-service.txt +++ b/lib/lp/services/webservice/stories/xx-service.txt @@ -13,7 +13,7 @@ The Launchpad web service defines three versions: 'beta', '1.0', and >>> def me_link_for_version(version): ... response = webservice.get("/", api_version=version) - ... print response.jsonBody()['me_link'] + ... print(response.jsonBody()['me_link']) >>> me_link_for_version('beta') http://api.launchpad.test/beta/people/+me @@ -26,7 +26,7 @@ The Launchpad web service defines three versions: 'beta', '1.0', and No other versions are available. - >>> print webservice.get("/", api_version="nosuchversion") + >>> print(webservice.get("/", api_version="nosuchversion")) HTTP/1.1 404 Not Found ... @@ -39,9 +39,9 @@ is treated as an anonymous request. >>> root = 'http://api.launchpad.test/beta' >>> body = anon_webservice.get(root).jsonBody() - >>> print body['projects_collection_link'] + >>> print(body['projects_collection_link']) http://api.launchpad.test/beta/projects - >>> print body['me_link'] + >>> print(body['me_link']) http://api.launchpad.test/beta/people/+me Normally, Launchpad will reject any call made with an unrecognized @@ -56,7 +56,7 @@ consumer keys. >>> response = caller.get(root) >>> response.status 401 - >>> print response.body + >>> print(response.body) Unknown consumer (new-consumer). But with anonymous access there is no registration step. The first @@ -67,7 +67,7 @@ doesn't recognize the client. >>> login(ANONYMOUS) >>> from zope.component import getUtility >>> consumer_set = getUtility(IOAuthConsumerSet) - >>> print consumer_set.getByKey(u'another-new-consumer') + >>> print(consumer_set.getByKey(u'another-new-consumer')) None >>> logout() @@ -81,7 +81,7 @@ Anonymous requests can't access certain data. >>> response = anon_webservice.get(body['me_link']) >>> response.status 401 - >>> print response.body + >>> print(response.body) You need to be logged in to view this URL. Anonymous requests can't change the dataset. @@ -92,7 +92,7 @@ Anonymous requests can't change the dataset. ... 'application/json', data) >>> response.status 401 - >>> print response.body + >>> print(response.body) (<Person at...>, 'display_name', 'launchpad.Edit') A completely unsigned web service request is treated as an anonymous @@ -100,7 +100,7 @@ request, with the OAuth consumer name being equal to the User-Agent. >>> agent = u"unsigned-user-agent" >>> login(ANONYMOUS) - >>> print consumer_set.getByKey(agent) + >>> print(consumer_set.getByKey(agent)) None >>> logout() @@ -116,7 +116,7 @@ request, with the OAuth consumer name being equal to the User-Agent. ... return http(request) >>> response = request_with_user_agent(agent) - >>> print response.getOutput() + >>> print(response.getOutput()) HTTP/1.1 200 Ok ... {...} @@ -125,7 +125,7 @@ An unsigned request, like a request signed with the empty string, isn't logged in as any particular user: >>> response = request_with_user_agent(agent, "/devel/people/+me") - >>> print response.getOutput() + >>> print(response.getOutput()) HTTP/1.1 401 Unauthorized ... You need to be logged in to view this URL. @@ -155,7 +155,7 @@ to a different host. >>> webservice.domain = 'bugs.launchpad.test' >>> root = webservice.get( ... 'http://bugs.launchpad.test/api/devel/').jsonBody() - >>> print root['people_collection_link'] + >>> print(root['people_collection_link']) http://bugs.launchpad.test/api/devel/people Requests on these hosts also honor the standard Launchpad authorization @@ -167,9 +167,9 @@ scheme (and don't require OAuth). >>> noauth_webservice = LaunchpadWebServiceCaller( ... domain='bugs.launchpad.test') >>> sample_auth = 'Basic %s' % base64.b64encode('[email protected]:test') - >>> print noauth_webservice.get( + >>> print(noauth_webservice.get( ... 'http://bugs.launchpad.test/api/devel/people/+me', - ... headers={'Authorization': sample_auth}) + ... headers={'Authorization': sample_auth})) HTTP/1.1 303 See Other ... Location: http://bugs.launchpad.test/api/devel/~name12... @@ -180,9 +180,9 @@ virtual host: an attempt to do HTTP Basic Auth will be treated as an anonymous request. >>> noauth_webservice.domain = 'api.launchpad.test' - >>> print noauth_webservice.get( + >>> print(noauth_webservice.get( ... 'http://api.launchpad.test/beta/people/+me', - ... headers={'Authorization': sample_auth}) + ... headers={'Authorization': sample_auth})) HTTP/1.1 401 Unauthorized ... You need to be logged in to view this URL. @@ -195,12 +195,12 @@ Launchpad's web service sets the Vary header differently from other parts of Launchpad. >>> browser.open("http://launchpad.test/") - >>> print browser.headers['Vary'] + >>> print(browser.headers['Vary']) Cookie, Authorization >>> response = webservice.get( ... 'http://bugs.launchpad.test/api/devel/') - >>> print response.getheader('Vary') + >>> print(response.getheader('Vary')) Accept The web service's Vary header does not mention the 'Cookie' header, diff --git a/lib/lp/services/webservice/stories/xx-wadl.txt b/lib/lp/services/webservice/stories/xx-wadl.txt index 6ce83e7..f7180ef 100644 --- a/lib/lp/services/webservice/stories/xx-wadl.txt +++ b/lib/lp/services/webservice/stories/xx-wadl.txt @@ -33,8 +33,8 @@ Let's write some fake WADL to disk. When we request the WADL for version "devel", the fake WADL is loaded from disk. - >>> print webservice.get( - ... '/', 'application/vd.sun.wadl+xml', api_version='devel').body + >>> print(webservice.get( + ... '/', 'application/vd.sun.wadl+xml', api_version='devel').body) Some fake WADL. The fake WADL is now present in the cache. @@ -46,8 +46,8 @@ Change the cached value, and we change the document served. >>> WebServiceApplication.cached_wadl['devel'] = "More fake WADL." - >>> print webservice.get( - ... '/', 'application/vd.sun.wadl+xml', api_version='devel').body + >>> print(webservice.get( + ... '/', 'application/vd.sun.wadl+xml', api_version='devel').body) More fake WADL. If there's no value in the cache and no cached file on disk, the WADL diff --git a/lib/lp/services/webservice/tests/test_doc.py b/lib/lp/services/webservice/tests/test_doc.py index ce59e0e..47384c7 100644 --- a/lib/lp/services/webservice/tests/test_doc.py +++ b/lib/lp/services/webservice/tests/test_doc.py @@ -15,9 +15,11 @@ from lp.testing.layers import ( AppServerLayer, LaunchpadFunctionalLayer, ) +from lp.testing.pages import setUpGlobs from lp.testing.systemdocs import ( LayeredDocFileSuite, setGlobs, + setUp, ) @@ -32,7 +34,9 @@ def layerlessTearDown(test): special = { 'webservice-configuration.txt': LayeredDocFileSuite( '../doc/webservice-configuration.txt', - setUp=setGlobs, tearDown=layerlessTearDown, layer=None), + setUp=lambda test: setGlobs(test, future=True), + tearDown=layerlessTearDown, + layer=None), # This test is actually run twice to prove that the AppServerLayer # properly isolates the database between tests. 'launchpadlib.txt': LayeredDocFileSuite( @@ -46,4 +50,7 @@ special = { def test_suite(): - return build_test_suite(here, special, layer=LaunchpadFunctionalLayer) + return build_test_suite( + here, special, setUp=lambda test: setUp(test, future=True), + pageTestsSetUp=lambda test: setUpGlobs(test, future=True), + layer=LaunchpadFunctionalLayer) diff --git a/lib/lp/services/webservice/tests/test_wadllib.py b/lib/lp/services/webservice/tests/test_wadllib.py index e963e44..10a7238 100644 --- a/lib/lp/services/webservice/tests/test_wadllib.py +++ b/lib/lp/services/webservice/tests/test_wadllib.py @@ -3,6 +3,8 @@ """Run the standalone wadllib tests.""" +from __future__ import absolute_import, print_function + __metaclass__ = type __all__ = ['test_suite'] @@ -19,6 +21,11 @@ from lp.testing.systemdocs import LayeredDocFileSuite topdir = os.path.dirname(wadllib.__file__) +def setUp(test): + for future_item in 'absolute_import', 'print_function': + test.globs[future_item] = getattr(globals(), future_item) + + def test_suite(): suite = unittest.TestSuite() @@ -36,7 +43,7 @@ def test_suite(): # Sort the tests. for filename in sorted(doctest_files): path = doctest_files[filename] - doctest = LayeredDocFileSuite(path, package=wadllib) + doctest = LayeredDocFileSuite(path, package=wadllib, setUp=setUp) suite.addTest(doctest) return suite
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : [email protected] Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp

