Merge lp:~xnox/lazr.restfulclient/fixes into lp:lazr.restfulclient

Proposed by Dimitri John Ledkov
Status: Merged
Merged at revision: 137
Proposed branch: lp:~xnox/lazr.restfulclient/fixes
Merge into: lp:lazr.restfulclient
Diff against target: 743 lines (+84/-310)
9 files modified
buildout.cfg (+1/-1)
ez_setup.py (+0/-241)
setup.py (+6/-7)
src/lazr/restfulclient/_browser.py (+14/-23)
src/lazr/restfulclient/authorize/oauth.py (+2/-1)
src/lazr/restfulclient/resource.py (+22/-12)
src/lazr/restfulclient/tests/test_atomicfilecache.py (+29/-15)
src/lazr/restfulclient/tests/test_error.py (+2/-2)
src/lazr/restfulclient/tests/test_oauth.py (+8/-8)
To merge this branch: bzr merge lp:~xnox/lazr.restfulclient/fixes
Reviewer Review Type Date Requested Status
Barry Warsaw Needs Information
LAZR Developers Pending
Review via email: mp+224945@code.launchpad.net

Commit message

Simplify setup.py and fix one python3 syntax error.

To post a comment you must log in.
lp:~xnox/lazr.restfulclient/fixes updated
139. By Dimitri John Ledkov

SSLHandshakeError is gone in python3, add compat.

140. By Dimitri John Ledkov

Resurect bootstrap.py

141. By Dimitri John Ledkov

Depreciation warning readfp -> read_file

142. By Dimitri John Ledkov

Take 1 at unicode

143. By Dimitri John Ledkov

More stuff

144. By Dimitri John Ledkov

Distch six

145. By Dimitri John Ledkov

Fix standalone buildout

146. By Dimitri John Ledkov

drop unused things

147. By Dimitri John Ledkov

Revert to minimise diff

148. By Dimitri John Ledkov

url must be string

149. By Dimitri John Ledkov

More crap

Revision history for this message
Barry Warsaw (barry) wrote :

A few minor comments and one important question. (inline of course)

review: Needs Information
Revision history for this message
Dimitri John Ledkov (xnox) wrote :

Replied.

Revision history for this message
Barry Warsaw (barry) wrote :

On Jul 14, 2014, at 10:20 PM, Dimitri John Ledkov wrote:

>> try:
>> + from httplib2 import SSLHandshakeError
>> +except ImportError:
>> + from httplib2 import HttpLib2Error as SSLHandshakeError
>> +try:
>
>yes, it does. But it's only used to catch the error, and re-raise it with a
>helpful message that "there is an option to ignore ssl certificate
>errors". The actual class of the error has changed, but i believe the text of
>the error comes from the underlying library. This keeps compat in python2,
>and the worst case scenario the error is re-raised regardless.

So I had a look at the actual code where this is used. Ug.

>Maybe I should remove all error catching and thus not use either
>SSLHandshakeError nor HttpLib2Error?

That does seem better. Then release notes or documentation can describe
LP_DISABLE_SSL_CERTIFICATE_VALIDATION.

lp:~xnox/lazr.restfulclient/fixes updated
150. By Dimitri John Ledkov

Whitespace and review fixes, drop SSLHandshakeError handling.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'buildout.cfg'
--- buildout.cfg 2012-06-19 13:28:22 +0000
+++ buildout.cfg 2014-07-28 15:39:09 +0000
@@ -17,7 +17,7 @@
1717
18[standalone_test]18[standalone_test]
19recipe = zc.recipe.testrunner19recipe = zc.recipe.testrunner
20eggs = lazr.restfulclient20eggs = lazr.restfulclient [test]
21defaults = '--tests-pattern ^tests --exit-with-status --suite-name standalone_tests'.split()21defaults = '--tests-pattern ^tests --exit-with-status --suite-name standalone_tests'.split()
2222
23[docs]23[docs]
2424
=== removed file 'ez_setup.py'
--- ez_setup.py 2010-03-24 15:14:12 +0000
+++ ez_setup.py 1970-01-01 00:00:00 +0000
@@ -1,241 +0,0 @@
1#!python
2"""Bootstrap setuptools installation
3
4If you want to use setuptools in your package's setup.py, just include this
5file in the same directory with it, and add this to the top of your setup.py::
6
7 from ez_setup import use_setuptools
8 use_setuptools()
9
10If you want to require a specific version of setuptools, set a download
11mirror, or use an alternate download directory, you can do so by supplying
12the appropriate options to ``use_setuptools()``.
13
14This file can also be run as a script to install or upgrade setuptools.
15"""
16import sys
17DEFAULT_VERSION = "0.6c11"
18DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
19
20md5_data = {
21 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
22 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
23 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
24 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
25 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
26 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
27 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
28 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
29 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
30 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
31 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
32 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
33 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
34 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
35 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
36 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
37 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
38 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
39 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
40 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
41 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
42 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
43 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
44 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
45 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
46 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
47 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
48 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
49 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
50 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
51}
52
53import sys, os
54
55def _validate_md5(egg_name, data):
56 if egg_name in md5_data:
57 from md5 import md5
58 digest = md5(data).hexdigest()
59 if digest != md5_data[egg_name]:
60 print >>sys.stderr, (
61 "md5 validation of %s failed! (Possible download problem?)"
62 % egg_name
63 )
64 sys.exit(2)
65 return data
66
67
68def use_setuptools(
69 version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
70 download_delay=15, min_version=None
71):
72 """Automatically find/download setuptools and make it available on sys.path
73
74 `version` should be a valid setuptools version number that is available
75 as an egg for download under the `download_base` URL (which should end with
76 a '/'). `to_dir` is the directory where setuptools will be downloaded, if
77 it is not already available. If `download_delay` is specified, it should
78 be the number of seconds that will be paused before initiating a download,
79 should one be required. If an older version of setuptools is installed,
80 this routine will print a message to ``sys.stderr`` and raise SystemExit in
81 an attempt to abort the calling script.
82 """
83 # Work around a hack in the ez_setup.py file from simplejson==1.7.3.
84 if min_version:
85 version = min_version
86
87 was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
88 def do_download():
89 egg = download_setuptools(version, download_base, to_dir, download_delay)
90 sys.path.insert(0, egg)
91 import setuptools; setuptools.bootstrap_install_from = egg
92 try:
93 import pkg_resources
94 except ImportError:
95 return do_download()
96 try:
97 pkg_resources.require("setuptools>="+version); return
98 except pkg_resources.VersionConflict, e:
99 if was_imported:
100 print >>sys.stderr, (
101 "The required version of setuptools (>=%s) is not available, and\n"
102 "can't be installed while this script is running. Please install\n"
103 " a more recent version first, using 'easy_install -U setuptools'."
104 "\n\n(Currently using %r)"
105 ) % (version, e.args[0])
106 sys.exit(2)
107 else:
108 del pkg_resources, sys.modules['pkg_resources'] # reload ok
109 return do_download()
110 except pkg_resources.DistributionNotFound:
111 return do_download()
112
113def download_setuptools(
114 version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
115 delay = 15
116):
117 """Download setuptools from a specified location and return its filename
118
119 `version` should be a valid setuptools version number that is available
120 as an egg for download under the `download_base` URL (which should end
121 with a '/'). `to_dir` is the directory where the egg will be downloaded.
122 `delay` is the number of seconds to pause before an actual download attempt.
123 """
124 import urllib2, shutil
125 egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
126 url = download_base + egg_name
127 saveto = os.path.join(to_dir, egg_name)
128 src = dst = None
129 if not os.path.exists(saveto): # Avoid repeated downloads
130 try:
131 from distutils import log
132 if delay:
133 log.warn("""
134---------------------------------------------------------------------------
135This script requires setuptools version %s to run (even to display
136help). I will attempt to download it for you (from
137%s), but
138you may need to enable firewall access for this script first.
139I will start the download in %d seconds.
140
141(Note: if this machine does not have network access, please obtain the file
142
143 %s
144
145and place it in this directory before rerunning this script.)
146---------------------------------------------------------------------------""",
147 version, download_base, delay, url
148 ); from time import sleep; sleep(delay)
149 log.warn("Downloading %s", url)
150 src = urllib2.urlopen(url)
151 # Read/write all in one block, so we don't create a corrupt file
152 # if the download is interrupted.
153 data = _validate_md5(egg_name, src.read())
154 dst = open(saveto,"wb"); dst.write(data)
155 finally:
156 if src: src.close()
157 if dst: dst.close()
158 return os.path.realpath(saveto)
159
160def main(argv, version=DEFAULT_VERSION):
161 """Install or upgrade setuptools and EasyInstall"""
162 try:
163 import setuptools
164 except ImportError:
165 egg = None
166 try:
167 egg = download_setuptools(version, delay=0)
168 sys.path.insert(0,egg)
169 from setuptools.command.easy_install import main
170 return main(list(argv)+[egg]) # we're done here
171 finally:
172 if egg and os.path.exists(egg):
173 os.unlink(egg)
174 else:
175 if setuptools.__version__ == '0.0.1':
176 print >>sys.stderr, (
177 "You have an obsolete version of setuptools installed. Please\n"
178 "remove it from your system entirely before rerunning this script."
179 )
180 sys.exit(2)
181
182 req = "setuptools>="+version
183 import pkg_resources
184 try:
185 pkg_resources.require(req)
186 except pkg_resources.VersionConflict:
187 try:
188 from setuptools.command.easy_install import main
189 except ImportError:
190 from easy_install import main
191 main(list(argv)+[download_setuptools(delay=0)])
192 sys.exit(0) # try to force an exit
193 else:
194 if argv:
195 from setuptools.command.easy_install import main
196 main(argv)
197 else:
198 print "Setuptools version",version,"or greater has been installed."
199 print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
200
201def update_md5(filenames):
202 """Update our built-in md5 registry"""
203
204 import re
205 from md5 import md5
206
207 for name in filenames:
208 base = os.path.basename(name)
209 f = open(name,'rb')
210 md5_data[base] = md5(f.read()).hexdigest()
211 f.close()
212
213 data = [" %r: %r,\n" % it for it in md5_data.items()]
214 data.sort()
215 repl = "".join(data)
216
217 import inspect
218 srcfile = inspect.getsourcefile(sys.modules[__name__])
219 f = open(srcfile, 'rb'); src = f.read(); f.close()
220
221 match = re.search("\nmd5_data = {\n([^}]+)}", src)
222 if not match:
223 print >>sys.stderr, "Internal error!"
224 sys.exit(2)
225
226 src = src[:match.start(1)] + repl + src[match.end(1):]
227 f = open(srcfile,'w')
228 f.write(src)
229 f.close()
230
231
232if __name__=='__main__':
233 if len(sys.argv)>2 and sys.argv[1]=='--md5update':
234 update_md5(sys.argv[2:])
235 else:
236 main(sys.argv[1:])
237
238
239
240
241
2420
=== modified file 'setup.py'
--- setup.py 2014-06-05 11:25:42 +0000
+++ setup.py 2014-07-28 15:39:09 +0000
@@ -16,9 +16,6 @@
16# You should have received a copy of the GNU Lesser General Public License16# You should have received a copy of the GNU Lesser General Public License
17# along with lazr.restfulclient. If not, see <http://www.gnu.org/licenses/>.17# along with lazr.restfulclient. If not, see <http://www.gnu.org/licenses/>.
1818
19import ez_setup
20ez_setup.use_setuptools()
21
22import sys19import sys
23from setuptools import setup, find_packages20from setuptools import setup, find_packages
2421
@@ -45,6 +42,7 @@
45 namespace_packages=['lazr'],42 namespace_packages=['lazr'],
46 packages=find_packages('src'),43 packages=find_packages('src'),
47 package_dir={'':'src'},44 package_dir={'':'src'},
45 package_data={'lazr.restfulclient': ['version.txt']},
48 include_package_data=True,46 include_package_data=True,
49 zip_safe=False,47 zip_safe=False,
50 maintainer='LAZR Developers',48 maintainer='LAZR Developers',
@@ -56,11 +54,9 @@
56 license='LGPL v3',54 license='LGPL v3',
57 install_requires=[55 install_requires=[
58 'httplib2',56 'httplib2',
59 'lazr.authentication',
60 'oauth',57 'oauth',
61 'setuptools',58 'setuptools',
62 'wadllib>=1.1.4',59 'wadllib>=1.1.4',
63 'wsgi_intercept',
64 ],60 ],
65 url='https://launchpad.net/lazr.restfulclient',61 url='https://launchpad.net/lazr.restfulclient',
66 download_url= 'https://launchpad.net/lazr.restfulclient/+download',62 download_url= 'https://launchpad.net/lazr.restfulclient/+download',
@@ -73,8 +69,11 @@
73 extras_require=dict(69 extras_require=dict(
74 docs=['Sphinx',70 docs=['Sphinx',
75 'z3c.recipe.sphinxdoc'],71 'z3c.recipe.sphinxdoc'],
76 test=['lazr.restful>=0.11.0',72 test=[
77 'van.testing'],73 'lazr.authentication',
74 'lazr.restful>=0.11.0',
75 'wsgi_intercept',
76 ],
78 ),77 ),
79 test_suite='lazr.restfulclient.tests',78 test_suite='lazr.restfulclient.tests',
80 )79 )
8180
=== modified file 'src/lazr/restfulclient/_browser.py'
--- src/lazr/restfulclient/_browser.py 2014-06-05 11:25:42 +0000
+++ src/lazr/restfulclient/_browser.py 2014-07-28 15:39:09 +0000
@@ -41,7 +41,6 @@
41from time import sleep41from time import sleep
42from httplib2 import (42from httplib2 import (
43 Http,43 Http,
44 SSLHandshakeError,
45 urlnorm,44 urlnorm,
46 )45 )
47try:46try:
@@ -86,7 +85,12 @@
86 """85 """
8786
88 try:87 try:
89 if re_url_scheme.match(filename):88 if isinstance(filename, unicode_type):
89 filename_match = filename.encode('utf-8')
90 else:
91 filename_match = filename
92
93 if re_url_scheme.match(filename_match):
90 if isinstance(filename, bytes):94 if isinstance(filename, bytes):
91 filename = filename.decode('utf-8')95 filename = filename.decode('utf-8')
92 filename = filename.encode('idna')96 filename = filename.encode('idna')
@@ -97,8 +101,8 @@
97 if isinstance(filename, unicode_type):101 if isinstance(filename, unicode_type):
98 filename = filename.encode('utf-8')102 filename = filename.encode('utf-8')
99 filemd5 = _md5(filename).hexdigest()103 filemd5 = _md5(filename).hexdigest()
100 filename = re_url_scheme.sub("", filename)104 filename = re_url_scheme.sub(b"", filename)
101 filename = re_slash.sub(",", filename)105 filename = re_slash.sub(b",", filename)
102106
103 # This is the part that we changed. In stock httplib2, the107 # This is the part that we changed. In stock httplib2, the
104 # filename is trimmed if it's longer than 200 characters, and then108 # filename is trimmed if it's longer than 200 characters, and then
@@ -118,7 +122,7 @@
118 maximum_length_before_md5_sum = maximum_filename_length - 32 - 1122 maximum_length_before_md5_sum = maximum_filename_length - 32 - 1
119 if len(filename) > maximum_length_before_md5_sum:123 if len(filename) > maximum_length_before_md5_sum:
120 filename=filename[:maximum_length_before_md5_sum]124 filename=filename[:maximum_length_before_md5_sum]
121 return ",".join((filename, filemd5))125 return ",".join((filename.decode('utf-8'), filemd5))
122126
123127
124def ssl_certificate_validation_disabled():128def ssl_certificate_validation_disabled():
@@ -162,7 +166,7 @@
162 def _request(self, conn, host, absolute_uri, request_uri, method, body,166 def _request(self, conn, host, absolute_uri, request_uri, method, body,
163 headers, redirections, cachekey):167 headers, redirections, cachekey):
164 """Use the authorizer to authorize an outgoing request."""168 """Use the authorizer to authorize an outgoing request."""
165 if headers.has_key('authorization'):169 if 'authorization' in headers:
166 # There's an authorization header left over from a170 # There's an authorization header left over from a
167 # previous request that resulted in a redirect. Resources171 # previous request that resulted in a redirect. Resources
168 # protected by OAuth or HTTP Digest must send a distinct172 # protected by OAuth or HTTP Digest must send a distinct
@@ -308,7 +312,6 @@
308 key = key + '-' + self.request_media_type312 key = key + '-' + self.request_media_type
309 return safename(key)313 return safename(key)
310314
311
312 def _getCachedHeader(self, uri, header):315 def _getCachedHeader(self, uri, header):
313 """Retrieve a cached value for an HTTP header."""316 """Retrieve a cached value for an HTTP header."""
314 (scheme, authority, request_uri, cachekey) = urlnorm(uri)317 (scheme, authority, request_uri, cachekey) = urlnorm(uri)
@@ -370,25 +373,15 @@
370 "don't have the server-side permission to see.")373 "don't have the server-side permission to see.")
371374
372 # Add extra headers for the request.375 # Add extra headers for the request.
373 headers = {'Accept' : media_type}376 headers = {'Accept': media_type}
374 if self.user_agent is not None:377 if self.user_agent is not None:
375 headers['User-Agent'] = self.user_agent378 headers['User-Agent'] = self.user_agent
376 if isinstance(self._connection.cache, MultipleRepresentationCache):379 if isinstance(self._connection.cache, MultipleRepresentationCache):
377 self._connection.cache.request_media_type = media_type380 self._connection.cache.request_media_type = media_type
378 if extra_headers is not None:381 if extra_headers is not None:
379 headers.update(extra_headers)382 headers.update(extra_headers)
380 # Make the request.383 response, content = self._request_and_retry(
381 try:384 str(url), method=method, body=data, headers=headers)
382 response, content = self._request_and_retry(
383 str(url), method=method, body=data, headers=headers)
384 except SSLHandshakeError, e:
385 msg = str(e)
386 if "SSL3_GET_SERVER_CERTIFICATE:certificate verify failed" in msg:
387 raise SSLHandshakeError(
388 "%s: perhaps set LP_DISABLE_SSL_CERTIFICATE_VALIDATION if "
389 "certificate validation failed and you genuinely trust the "
390 "remote server." % (e,))
391 raise
392 if response.status == 304:385 if response.status == 304:
393 # The resource didn't change.386 # The resource didn't change.
394 if content == '':387 if content == '':
@@ -447,10 +440,8 @@
447 wadl_type = 'application/vnd.sun.wadl+xml'440 wadl_type = 'application/vnd.sun.wadl+xml'
448 response, content = self._request(url, media_type=wadl_type)441 response, content = self._request(url, media_type=wadl_type)
449 url = str(url)442 url = str(url)
450 if not isinstance(url, bytes):
451 url = url.encode('utf-8')
452 if not isinstance(content, bytes):443 if not isinstance(content, bytes):
453 content = content.encode('utf-8') 444 content = content.encode('utf-8')
454 return Application(url, content)445 return Application(url, content)
455446
456 def post(self, url, method_name, **kws):447 def post(self, url, method_name, **kws):
457448
=== modified file 'src/lazr/restfulclient/authorize/oauth.py'
--- src/lazr/restfulclient/authorize/oauth.py 2012-06-04 19:54:58 +0000
+++ src/lazr/restfulclient/authorize/oauth.py 2014-07-28 15:39:09 +0000
@@ -159,7 +159,8 @@
159 """159 """
160 # Attempt to load the access token from the file.160 # Attempt to load the access token from the file.
161 parser = SafeConfigParser()161 parser = SafeConfigParser()
162 parser.readfp(readable_file)162 reader = getattr(parser, 'read_file', parser.readfp)
163 reader(readable_file)
163 # Check the version number and extract the access token and164 # Check the version number and extract the access token and
164 # secret. Then convert these to the appropriate instances.165 # secret. Then convert these to the appropriate instances.
165 if not parser.has_section(CREDENTIALS_FILE_VERSION):166 if not parser.has_section(CREDENTIALS_FILE_VERSION):
166167
=== modified file 'src/lazr/restfulclient/resource.py'
--- src/lazr/restfulclient/resource.py 2014-06-06 23:21:56 +0000
+++ src/lazr/restfulclient/resource.py 2014-07-28 15:39:09 +0000
@@ -55,6 +55,14 @@
55import types55import types
56import urllib56import urllib
5757
58import sys
59if sys.version_info[0] >= 3:
60 text_type = str
61 binary_type = bytes
62else:
63 text_type = unicode
64 binary_type = str
65
58from lazr.uri import URI66from lazr.uri import URI
59from wadllib.application import Resource as WadlResource67from wadllib.application import Resource as WadlResource
6068
@@ -371,7 +379,9 @@
371 if self._wadl_resource.representation is None:379 if self._wadl_resource.representation is None:
372 # Get a representation of the linked resource.380 # Get a representation of the linked resource.
373 representation = self._root._browser.get(self._wadl_resource)381 representation = self._root._browser.get(self._wadl_resource)
374 representation = loads(unicode(representation))382 if isinstance(representation, binary_type):
383 representation = representation.decode('utf-8')
384 representation = loads(text_type(representation))
375385
376 # In rare cases, the resource type served by the386 # In rare cases, the resource type served by the
377 # server conflicts with the type the client thought387 # server conflicts with the type the client thought
@@ -381,7 +391,7 @@
381 # XXX This should probably be moved into a hook method391 # XXX This should probably be moved into a hook method
382 # defined by Entry, since it's not relevant to other392 # defined by Entry, since it's not relevant to other
383 # resource types.393 # resource types.
384 if type(representation) == types.DictType:394 if isinstance(representation, dict):
385 type_link = representation['resource_type_link']395 type_link = representation['resource_type_link']
386 if (type_link is not None396 if (type_link is not None
387 and type_link != self._wadl_resource.type_url):397 and type_link != self._wadl_resource.type_url):
@@ -521,7 +531,7 @@
521 url = str(self._root_uri.append(url))531 url = str(self._root_uri.append(url))
522 document = self._browser.get(url)532 document = self._browser.get(url)
523 try:533 try:
524 representation = loads(unicode(document))534 representation = loads(text_type(document))
525 except ValueError:535 except ValueError:
526 raise ValueError("%s doesn't serve a JSON document." % url)536 raise ValueError("%s doesn't serve a JSON document." % url)
527 type_link = representation.get("resource_type_link")537 type_link = representation.get("resource_type_link")
@@ -574,7 +584,7 @@
574 # and option values (because JSON-encoding them will screw584 # and option values (because JSON-encoding them will screw
575 # up wadllib's parameter validation). The option value thing585 # up wadllib's parameter validation). The option value thing
576 # is a little hacky, but it's the best solution for now.586 # is a little hacky, but it's the best solution for now.
577 if not key in send_as_is_params:587 if key not in send_as_is_params:
578 args[key] = dumps(value, cls=DatetimeJSONEncoder)588 args[key] = dumps(value, cls=DatetimeJSONEncoder)
579 if http_method in ('get', 'head', 'delete'):589 if http_method in ('get', 'head', 'delete'):
580 url = self.wadl_method.build_request_url(**args)590 url = self.wadl_method.build_request_url(**args)
@@ -616,7 +626,7 @@
616 HeaderDictionary(response))626 HeaderDictionary(response))
617 wadl_parameter = wadl_response.get_parameter('Location')627 wadl_parameter = wadl_response.get_parameter('Location')
618 wadl_resource = wadl_parameter.linked_resource628 wadl_resource = wadl_parameter.linked_resource
619 # Fetch a representation of the new resource.629 # Fetch a representation of the new resource.
620 response, content = self.root._browser._request(630 response, content = self.root._browser._request(
621 wadl_resource.url)631 wadl_resource.url)
622 # Return an instance of the appropriate lazr.restful632 # Return an instance of the appropriate lazr.restful
@@ -637,13 +647,13 @@
637 # The operation returned a document with nothing647 # The operation returned a document with nothing
638 # special about it.648 # special about it.
639 if content_type == self.JSON_MEDIA_TYPE:649 if content_type == self.JSON_MEDIA_TYPE:
640 return loads(unicode(content))650 return loads(text_type(content))
641 # We don't know how to process the content.651 # We don't know how to process the content.
642 return content652 return content
643653
644 # The operation returned a representation of some654 # The operation returned a representation of some
645 # resource. Instantiate a Resource object for it.655 # resource. Instantiate a Resource object for it.
646 document = loads(unicode(content))656 document = loads(text_type(content))
647 if document is None:657 if document is None:
648 # The operation returned a null value.658 # The operation returned a null value.
649 return document659 return document
@@ -767,7 +777,7 @@
767 if response.status == 209 and content_type == self.JSON_MEDIA_TYPE:777 if response.status == 209 and content_type == self.JSON_MEDIA_TYPE:
768 # The server sent back a new representation of the object.778 # The server sent back a new representation of the object.
769 # Use it in preference to the existing representation.779 # Use it in preference to the existing representation.
770 new_representation = loads(unicode(content))780 new_representation = loads(text_type(content))
771 self._wadl_resource.representation = new_representation781 self._wadl_resource.representation = new_representation
772 self._wadl_resource.media_type = content_type782 self._wadl_resource.media_type = content_type
773783
@@ -786,7 +796,7 @@
786 :rtype: int796 :rtype: int
787 """797 """
788 total_size = self.total_size798 total_size = self.total_size
789 if type(total_size) == types.IntType:799 if isinstance(total_size, int):
790 # The size was a number present in the collection800 # The size was a number present in the collection
791 # representation.801 # representation.
792 return total_size802 return total_size
@@ -813,7 +823,7 @@
813 if next_link is None:823 if next_link is None:
814 break824 break
815 current_page = loads(825 current_page = loads(
816 unicode(self._root._browser.get(URI(next_link))))826 text_type(self._root._browser.get(URI(next_link))))
817827
818 def __getitem__(self, key):828 def __getitem__(self, key):
819 """Look up a slice, or a subordinate resource by index.829 """Look up a slice, or a subordinate resource by index.
@@ -886,7 +896,7 @@
886 # Iterate over pages until we have the correct number of entries.896 # Iterate over pages until we have the correct number of entries.
887 while more_needed > 0 and page_url is not None:897 while more_needed > 0 and page_url is not None:
888 representation = loads(898 representation = loads(
889 unicode(self._root._browser.get(page_url)))899 text_type(self._root._browser.get(page_url)))
890 current_page_entries = representation['entries']900 current_page_entries = representation['entries']
891 entry_dicts += current_page_entries[:more_needed]901 entry_dicts += current_page_entries[:more_needed]
892 more_needed = desired_size - len(entry_dicts)902 more_needed = desired_size - len(entry_dicts)
@@ -1011,7 +1021,7 @@
1011 # the resource describes itself.1021 # the resource describes itself.
1012 try:1022 try:
1013 representation = loads(1023 representation = loads(
1014 unicode(self._root._browser.get(url)))1024 text_type(self._root._browser.get(url)))
1015 except HTTPError as error:1025 except HTTPError as error:
1016 # There's no resource corresponding to the given ID.1026 # There's no resource corresponding to the given ID.
1017 if error.response.status == 404:1027 if error.response.status == 404:
10181028
=== modified file 'src/lazr/restfulclient/tests/test_atomicfilecache.py'
--- src/lazr/restfulclient/tests/test_atomicfilecache.py 2012-06-04 19:54:58 +0000
+++ src/lazr/restfulclient/tests/test_atomicfilecache.py 2014-07-28 15:39:09 +0000
@@ -23,6 +23,13 @@
23import tempfile23import tempfile
24import unittest24import unittest
2525
26import sys
27PY3 = sys.version_info[0] >= 3
28if PY3:
29 binary_type = bytes
30else:
31 binary_type = str
32
26import httplib233import httplib2
2734
28from lazr.restfulclient._browser import AtomicFileCache35from lazr.restfulclient._browser import AtomicFileCache
@@ -56,15 +63,15 @@
56 def test_set_key(self):63 def test_set_key(self):
57 # A key set with set() can be got by get().64 # A key set with set() can be got by get().
58 cache = self.make_file_cache()65 cache = self.make_file_cache()
59 cache.set('key', 'value')66 cache.set('key', b'value')
60 self.assertEqual('value', cache.get('key'))67 self.assertEqual(b'value', cache.get('key'))
6168
62 def test_set_twice_overrides(self):69 def test_set_twice_overrides(self):
63 # Setting a key again overrides the value.70 # Setting a key again overrides the value.
64 cache = self.make_file_cache()71 cache = self.make_file_cache()
65 cache.set('key', 'value')72 cache.set('key', b'value')
66 cache.set('key', 'new-value')73 cache.set('key', b'new-value')
67 self.assertEqual('new-value', cache.get('key'))74 self.assertEqual(b'new-value', cache.get('key'))
6875
69 def test_delete_absent_key(self):76 def test_delete_absent_key(self):
70 # Deleting a key that's not there does nothing.77 # Deleting a key that's not there does nothing.
@@ -76,7 +83,7 @@
76 # A key once set can be deleted. Further attempts to get that key83 # A key once set can be deleted. Further attempts to get that key
77 # return None.84 # return None.
78 cache = self.make_file_cache()85 cache = self.make_file_cache()
79 cache.set('key', 'value')86 cache.set('key', b'value')
80 cache.delete('key')87 cache.delete('key')
81 self.assertIs(None, cache.get('key'))88 self.assertIs(None, cache.get('key'))
8289
@@ -101,7 +108,7 @@
101 # probably a bug in httplib2.FileCache.108 # probably a bug in httplib2.FileCache.
102 cache = self.make_file_cache()109 cache = self.make_file_cache()
103 self.assertRaises(TypeError, cache.set, 'answer', 42)110 self.assertRaises(TypeError, cache.set, 'answer', 42)
104 self.assertEqual('', cache.get('answer'))111 self.assertEqual(b'', cache.get('answer'))
105112
106 def test_get_unicode(self):113 def test_get_unicode(self):
107 # get() can retrieve unicode keys.114 # get() can retrieve unicode keys.
@@ -110,19 +117,20 @@
110117
111 def test_set_unicode_keys(self):118 def test_set_unicode_keys(self):
112 cache = self.make_file_cache()119 cache = self.make_file_cache()
113 cache.set(self.unicode_text, 'value')120 cache.set(self.unicode_text, b'value')
114 self.assertEqual('value', cache.get(self.unicode_text))121 self.assertEqual(b'value', cache.get(self.unicode_text))
115122
116 def test_set_unicode_value(self):123 def test_set_unicode_value(self):
117 # set() cannot store unicode values. Values must be bytes.124 # set() cannot store unicode values. Values must be bytes.
118 cache = self.make_file_cache()125 cache = self.make_file_cache()
126 error = TypeError if PY3 else UnicodeEncodeError
119 self.assertRaises(127 self.assertRaises(
120 UnicodeEncodeError, cache.set, 'key', self.unicode_text)128 error, cache.set, 'key', self.unicode_text)
121129
122 def test_delete_unicode(self):130 def test_delete_unicode(self):
123 # delete() can remove unicode keys.131 # delete() can remove unicode keys.
124 cache = self.make_file_cache()132 cache = self.make_file_cache()
125 cache.set(self.unicode_text, 'value')133 cache.set(self.unicode_text, b'value')
126 cache.delete(self.unicode_text)134 cache.delete(self.unicode_text)
127 self.assertIs(None, cache.get(self.unicode_text))135 self.assertIs(None, cache.get(self.unicode_text))
128136
@@ -132,6 +140,12 @@
132140
133 file_cache_factory = AtomicFileCache141 file_cache_factory = AtomicFileCache
134142
143 @staticmethod
144 def prefix_safename(x):
145 if isinstance(x, binary_type):
146 x = x.decode('utf-8')
147 return AtomicFileCache.TEMPFILE_PREFIX + x
148
135 def test_set_non_string_value(self):149 def test_set_non_string_value(self):
136 # set() raises TypeError if asked to set a key to a non-string value.150 # set() raises TypeError if asked to set a key to a non-string value.
137 # Attempts to retrieve that value act is if it were never set.151 # Attempts to retrieve that value act is if it were never set.
@@ -144,16 +158,16 @@
144 # Implementation-specific tests follow.158 # Implementation-specific tests follow.
145159
146 def test_bad_safename_get(self):160 def test_bad_safename_get(self):
147 safename = lambda x: AtomicFileCache.TEMPFILE_PREFIX + x161 safename = self.prefix_safename
148 cache = AtomicFileCache(self.cache_dir, safename)162 cache = AtomicFileCache(self.cache_dir, safename)
149 self.assertRaises(ValueError, cache.get, 'key')163 self.assertRaises(ValueError, cache.get, 'key')
150164
151 def test_bad_safename_set(self):165 def test_bad_safename_set(self):
152 safename = lambda x: AtomicFileCache.TEMPFILE_PREFIX + x166 safename = self.prefix_safename
153 cache = AtomicFileCache(self.cache_dir, safename)167 cache = AtomicFileCache(self.cache_dir, safename)
154 self.assertRaises(ValueError, cache.set, 'key', 'value')168 self.assertRaises(ValueError, cache.set, 'key', b'value')
155169
156 def test_bad_safename_delete(self):170 def test_bad_safename_delete(self):
157 safename = lambda x: AtomicFileCache.TEMPFILE_PREFIX + x171 safename = self.prefix_safename
158 cache = AtomicFileCache(self.cache_dir, safename)172 cache = AtomicFileCache(self.cache_dir, safename)
159 self.assertRaises(ValueError, cache.delete, 'key')173 self.assertRaises(ValueError, cache.delete, 'key')
160174
=== modified file 'src/lazr/restfulclient/tests/test_error.py'
--- src/lazr/restfulclient/tests/test_error.py 2010-07-19 14:15:11 +0000
+++ src/lazr/restfulclient/tests/test_error.py 2014-07-28 15:39:09 +0000
@@ -39,10 +39,10 @@
39 request = DummyRequest(status)39 request = DummyRequest(status)
40 error = error_for(request, content)40 error = error_for(request, content)
41 if expected_error is None:41 if expected_error is None:
42 self.assertEquals(error, None)42 self.assertIsNone(error)
43 else:43 else:
44 self.assertTrue(isinstance(error, expected_error))44 self.assertTrue(isinstance(error, expected_error))
45 self.assertEquals(content, error.content)45 self.assertEqual(content, error.content)
4646
47 def test_no_error_for_2xx(self):47 def test_no_error_for_2xx(self):
48 """Make sure a 2xx response code yields no error."""48 """Make sure a 2xx response code yields no error."""
4949
=== modified file 'src/lazr/restfulclient/tests/test_oauth.py'
--- src/lazr/restfulclient/tests/test_oauth.py 2010-10-28 22:22:32 +0000
+++ src/lazr/restfulclient/tests/test_oauth.py 2014-07-28 15:39:09 +0000
@@ -39,14 +39,14 @@
3939
40 def test_data_fields(self):40 def test_data_fields(self):
41 consumer = Consumer("key", "secret", "application")41 consumer = Consumer("key", "secret", "application")
42 self.assertEquals(consumer.key, "key")42 self.assertEqual(consumer.key, "key")
43 self.assertEquals(consumer.secret, "secret")43 self.assertEqual(consumer.secret, "secret")
44 self.assertEquals(consumer.application_name, "application")44 self.assertEqual(consumer.application_name, "application")
4545
46 def test_default_application_name(self):46 def test_default_application_name(self):
47 # Application name defaults to None47 # Application name defaults to None
48 consumer = Consumer("key", "secret")48 consumer = Consumer("key", "secret")
49 self.assertEquals(consumer.application_name, None)49 self.assertEqual(consumer.application_name, None)
5050
5151
52class TestSystemWideConsumer(unittest.TestCase):52class TestSystemWideConsumer(unittest.TestCase):
@@ -105,7 +105,7 @@
105 self._set_platform(('Fooix', 'String2', 'String3'), 'FooOS')105 self._set_platform(('Fooix', 'String2', 'String3'), 'FooOS')
106 self._set_hostname("foo")106 self._set_hostname("foo")
107 consumer = oauth.SystemWideConsumer("app name")107 consumer = oauth.SystemWideConsumer("app name")
108 self.assertEquals(108 self.assertEqual(
109 consumer.key, 'System-wide: Fooix (foo)')109 consumer.key, 'System-wide: Fooix (foo)')
110110
111 def test_empty_linux_distribution(self):111 def test_empty_linux_distribution(self):
@@ -115,7 +115,7 @@
115 self._set_platform(('', '', ''), 'BarOS')115 self._set_platform(('', '', ''), 'BarOS')
116 self._set_hostname("bar")116 self._set_hostname("bar")
117 consumer = oauth.SystemWideConsumer("app name")117 consumer = oauth.SystemWideConsumer("app name")
118 self.assertEquals(118 self.assertEqual(
119 consumer.key, 'System-wide: BarOS (bar)')119 consumer.key, 'System-wide: BarOS (bar)')
120120
121 def test_broken_linux_distribution(self):121 def test_broken_linux_distribution(self):
@@ -125,7 +125,7 @@
125 self._set_platform(self._broken, 'BazOS')125 self._set_platform(self._broken, 'BazOS')
126 self._set_hostname("baz")126 self._set_hostname("baz")
127 consumer = oauth.SystemWideConsumer("app name")127 consumer = oauth.SystemWideConsumer("app name")
128 self.assertEquals(128 self.assertEqual(
129 consumer.key, 'System-wide: BazOS (baz)')129 consumer.key, 'System-wide: BazOS (baz)')
130130
131131
@@ -150,7 +150,7 @@
150150
151 # Make sure the file is readable and writable by the user, but151 # Make sure the file is readable and writable by the user, but
152 # not by anyone else.152 # not by anyone else.
153 self.assertEquals(stat.S_IMODE(os.stat(credentials_path).st_mode),153 self.assertEqual(stat.S_IMODE(os.stat(credentials_path).st_mode),
154 stat.S_IREAD | stat.S_IWRITE)154 stat.S_IREAD | stat.S_IWRITE)
155155
156 loaded_credentials = OAuthAuthorizer.load_from_path(credentials_path)156 loaded_credentials = OAuthAuthorizer.load_from_path(credentials_path)

Subscribers

People subscribed via source and target branches