From 6f58f7f690ec0f989fa5962a10e29984e4c1f5a4 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 28 May 2013 15:19:35 -0400 Subject: [PATCH 01/27] Document / test supported python versions. - Add Trove classifiers for supported Python versions. - Test supported Python versions using tox. --- .gitignore | 4 ++++ setup.cfg | 5 +++++ setup.py | 7 +++++++ tox.ini | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index 74df2303..01a9178a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ *.py? *.egg-info *.swp +.coverage +coverage.xml +nosetests.xml +.tox diff --git a/setup.cfg b/setup.cfg index e69de29b..1266c15f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -0,0 +1,5 @@ +[nosetests] +nocapture=1 +cover-package=oauth2 +cover-erase=1 + diff --git a/setup.py b/setup.py index acc41e17..ca12eaa7 100755 --- a/setup.py +++ b/setup.py @@ -31,6 +31,13 @@ author="Joe Stump", author_email="joe@simplegeo.com", url="http://github.com/simplegeo/python-oauth2", + classifiers=[ + "Intended Audience :: Developers", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: Implementation :: CPython", + ], packages = find_packages(), install_requires = ['httplib2'], license = "MIT License", diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..2f886e15 --- /dev/null +++ b/tox.ini @@ -0,0 +1,38 @@ +[tox] +envlist = +# py26,py27,py32,py33,pypy,cover,docs + py26,py27,cover + +[testenv] +commands = + python setup.py test -q +deps = + httplib2 + coverage + mock + +[testenv:cover] +basepython = + python2.7 +commands = + nosetests --with-xunit --with-xcoverage +deps = + httplib2 + coverage + mock + nose + nosexcover + +# we separate coverage into its own testenv because a) "last run wins" wrt +# cobertura jenkins reporting and b) pypy and jython can't handle any +# combination of versions of coverage and nosexcover that i can find. + +[testenv:docs] +basepython = + python2.6 +commands = + sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html + sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest +deps = + Sphinx + repoze.sphinx.autointerface From 55fb8f65a26cf2b00bc043aded1756adfaf4c4f1 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 28 May 2013 15:21:13 -0400 Subject: [PATCH 02/27] Coverage for o._version. --- oauth2/_version.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/oauth2/_version.py b/oauth2/_version.py index 9d779eaa..a0e9e36d 100644 --- a/oauth2/_version.py +++ b/oauth2/_version.py @@ -11,8 +11,9 @@ verstr = manual_verstr + "." + auto_build_num try: from pyutil.version_class import Version as pyutil_Version - __version__ = pyutil_Version(verstr) -except (ImportError, ValueError): +except (ImportError, ValueError): #pragma NO COVER # Maybe there is no pyutil installed. from distutils.version import LooseVersion as distutils_Version __version__ = distutils_Version(verstr) +else: #pragma NO COVER + __version__ = pyutil_Version(verstr) From 16e45f8111593da1f9987036fece7cd91a83aa08 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 28 May 2013 15:25:39 -0400 Subject: [PATCH 03/27] Python 2.5 is still supported. --- setup.py | 1 + tox.ini | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index ca12eaa7..98f42783 100755 --- a/setup.py +++ b/setup.py @@ -34,6 +34,7 @@ classifiers=[ "Intended Audience :: Developers", "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.5", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: Implementation :: CPython", diff --git a/tox.ini b/tox.ini index 2f886e15..6e38d503 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = # py26,py27,py32,py33,pypy,cover,docs - py26,py27,cover + py25,py26,py27,cover [testenv] commands = From cd1a95a46c701b4101da40220f041e5f91198bbb Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 28 May 2013 16:36:37 -0400 Subject: [PATCH 04/27] Restore 100% coverage. --- oauth2/__init__.py | 47 ++++++++------------ tests/test_oauth.py | 103 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 106 insertions(+), 44 deletions(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 835270e3..25d62a52 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -33,15 +33,15 @@ try: from urlparse import parse_qs - parse_qs # placate pyflakes -except ImportError: +except ImportError: #pragma NO COVER # fall back for Python 2.5 from cgi import parse_qs +else: #pragma NO COVER + parse_qs # placate pyflakes try: - from hashlib import sha1 - sha = sha1 -except ImportError: + from hashlib import sha1 as sha +except ImportError: #pragma NO COVER # hashlib was added in Python 2.5 import sha @@ -415,7 +415,7 @@ def to_url(self): base_url = urlparse.urlparse(self.url) try: query = base_url.query - except AttributeError: + except AttributeError: #pragma NO COVER # must be python <2.5 query = base_url[4] query = parse_qs(query) @@ -428,7 +428,7 @@ def to_url(self): path = base_url.path params = base_url.params fragment = base_url.fragment - except AttributeError: + except AttributeError: #pragma NO COVER # must be python <2.5 scheme = base_url[0] netloc = base_url[1] @@ -661,14 +661,8 @@ def request(self, uri, method="GET", body='', headers=None, req.sign_request(self.method, self.consumer, self.token) - schema, rest = urllib.splittype(uri) - if rest.startswith('//'): - hierpart = '//' - else: - hierpart = '' - host, rest = urllib.splithost(rest) - - realm = schema + ':' + hierpart + host + scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri) + realm = urlparse.urlunparse((scheme, netloc, '', None, None, None)) if is_form_encoded: body = req.to_postdata() @@ -731,31 +725,24 @@ def _get_version(self, request): def _get_signature_method(self, request): """Figure out the signature with some defaults.""" - try: - signature_method = request.get_parameter('oauth_signature_method') - except: + signature_method = request.get('oauth_signature_method') + if signature_method is None: signature_method = SIGNATURE_METHOD try: # Get the signature method object. - signature_method = self.signature_methods[signature_method] - except: + return self.signature_methods[signature_method] + except KeyError: signature_method_names = ', '.join(self.signature_methods.keys()) raise Error('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names)) - return signature_method - - def _get_verifier(self, request): - return request.get_parameter('oauth_verifier') - def _check_signature(self, request, consumer, token): timestamp, nonce = request._get_timestamp_nonce() self._check_timestamp(timestamp) signature_method = self._get_signature_method(request) - try: - signature = request.get_parameter('oauth_signature') - except: + signature = request.get('oauth_signature') + if signature is None: raise MissingSignature('Missing oauth_signature.') # Validate the signature. @@ -787,7 +774,7 @@ class SignatureMethod(object): provide a new way to sign requests. """ - def signing_base(self, request, consumer, token): + def signing_base(self, request, consumer, token): #pragma NO COVER """Calculates the string that needs to be signed. This method returns a 2-tuple containing the starting key for the @@ -797,7 +784,7 @@ def signing_base(self, request, consumer, token): """ raise NotImplementedError - def sign(self, request, consumer, token): + def sign(self, request, consumer, token): #pragma NO COVER """Returns the signature for the given request, based on the consumer and token also provided. diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 099e5794..d6dd1370 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -257,6 +257,7 @@ def failUnlessReallyEqual(self, a, b, msg=None): class TestFuncs(unittest.TestCase): def test_to_unicode(self): + self.failUnlessRaises(TypeError, oauth.to_unicode, 0) self.failUnlessRaises(TypeError, oauth.to_unicode, '\xae') self.failUnlessRaises(TypeError, oauth.to_unicode_optional_iterator, '\xae') self.failUnlessRaises(TypeError, oauth.to_unicode_optional_iterator, ['\xae']) @@ -264,30 +265,78 @@ def test_to_unicode(self): self.failUnlessEqual(oauth.to_unicode(':-)'), u':-)') self.failUnlessEqual(oauth.to_unicode(u'\u00ae'), u'\u00ae') self.failUnlessEqual(oauth.to_unicode('\xc2\xae'), u'\u00ae') - self.failUnlessEqual(oauth.to_unicode_optional_iterator([':-)']), [u':-)']) - self.failUnlessEqual(oauth.to_unicode_optional_iterator([u'\u00ae']), [u'\u00ae']) + + def test_to_utf8(self): + self.failUnlessRaises(TypeError, oauth.to_utf8, 0) + self.failUnlessRaises(TypeError, oauth.to_utf8, '\x81') + self.failUnlessEqual(oauth.to_utf8(':-)'), ':-)') + self.failUnlessEqual(oauth.to_utf8(u'\u00ae'), + u'\u00ae'.encode('utf8')) + + def test_to_unicode_if_string(self): + self.failUnless(oauth.to_unicode_if_string(self) is self) + self.failUnlessEqual(oauth.to_unicode_if_string(':-)'), u':-)') + + def test_to_utf8_if_string(self): + self.failUnless(oauth.to_utf8_if_string(self) is self) + self.failUnlessEqual(oauth.to_utf8_if_string(u':-)'), u':-)') + self.failUnlessEqual(oauth.to_utf8_if_string(u'\u00ae'), + u'\u00ae'.encode('utf8')) + + def test_to_unicode_optional_iterator(self): + self.failUnlessEqual(oauth.to_unicode_optional_iterator(':-)'), + u':-)') + self.failUnlessEqual(oauth.to_unicode_optional_iterator(u'\u00ae'), + u'\u00ae') + self.failUnlessEqual(oauth.to_unicode_optional_iterator([':-)']), + [u':-)']) + self.failUnlessEqual(oauth.to_unicode_optional_iterator([u'\u00ae']), + [u'\u00ae']) + self.failUnlessEqual(oauth.to_unicode_optional_iterator((u'\u00ae',)), + [u'\u00ae']) + self.failUnless(oauth.to_unicode_optional_iterator(self) is self) + + def test_to_utf8_optional_iterator(self): + self.failUnlessEqual(oauth.to_utf8_optional_iterator(':-)'), + ':-)') + self.failUnlessEqual(oauth.to_utf8_optional_iterator(u'\u00ae'), + u'\u00ae'.encode('utf8')) + self.failUnlessEqual(oauth.to_utf8_optional_iterator([':-)']), + [u':-)']) + self.failUnlessEqual(oauth.to_utf8_optional_iterator([u'\u00ae']), + [u'\u00ae'.encode('utf8')]) + self.failUnlessEqual(oauth.to_utf8_optional_iterator((u'\u00ae',)), + [u'\u00ae'.encode('utf8')]) + self.failUnless(oauth.to_utf8_optional_iterator(self) is self) class TestRequest(unittest.TestCase, ReallyEqualMixin): + def test__init__(self): + method = "GET" + req = oauth.Request(method) + self.assertFalse('url' in req.__dict__) + self.assertFalse('normalized_url' in req.__dict__) + self.assertRaises(AttributeError, getattr, req, 'url') + self.assertRaises(AttributeError, getattr, req, 'normalized_url') + def test_setter(self): url = "http://example.com" method = "GET" - req = oauth.Request(method) - self.assertTrue(not hasattr(req, 'url') or req.url is None) - self.assertTrue(not hasattr(req, 'normalized_url') or req.normalized_url is None) + req = oauth.Request(method, url) + self.assertEqual(req.url, url) + self.assertEqual(req.normalized_url, url) + req.url = url + '/?foo=bar' + self.assertEqual(req.url, url + '/?foo=bar') + self.assertEqual(req.normalized_url, url + '/') + req.url = None + self.assertEqual(req.url, None) + self.assertEqual(req.normalized_url, None) def test_deleter(self): url = "http://example.com" method = "GET" req = oauth.Request(method, url) - - try: - del req.url - url = req.url - self.fail("AttributeError should have been raised on empty url.") - except AttributeError: - pass - except Exception, e: - self.fail(str(e)) + del req.url + self.assertRaises(AttributeError, getattr, req, 'url') def test_url(self): url1 = "http://example.com:80/foo.php" @@ -995,6 +1044,32 @@ def test_verify_request(self): self.assertEquals(parameters['foo'], 59) self.assertEquals(parameters['multi'], ['FOO','BAR']) + def test_verify_request_missing_signature(self): + from oauth2 import MissingSignature + server = oauth.Server() + server.add_signature_method(oauth.SignatureMethod_PLAINTEXT()) + del self.request['oauth_signature_method'] + del self.request['oauth_signature'] + + self.assertRaises(MissingSignature, + server.verify_request, self.request, self.consumer, self.token) + + def test_verify_request_invalid_signature(self): + server = oauth.Server() + server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) + self.request['oauth_signature'] = 'BOGUS' + + self.assertRaises(oauth.Error, + server.verify_request, self.request, self.consumer, self.token) + + def test_verify_request_invalid_timestamp(self): + server = oauth.Server() + server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) + self.request['oauth_timestamp'] -= 86400 + + self.assertRaises(oauth.Error, + server.verify_request, self.request, self.consumer, self.token) + def test_build_authenticate_header(self): server = oauth.Server() headers = server.build_authenticate_header('example.com') From 457d64b42b74e8810520deeaa6a09f5adc24582f Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 28 May 2013 16:51:14 -0400 Subject: [PATCH 05/27] Py3k: print statements. --- example/client.py | 54 +++++++++++++++++++++++------------------------ example/server.py | 4 ++-- setup.py | 2 +- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/example/client.py b/example/client.py index 34f7dcb9..b050e05f 100644 --- a/example/client.py +++ b/example/client.py @@ -88,7 +88,7 @@ def access_resource(self, oauth_request): def run_example(): # setup - print '** OAuth Python Library Example **' + print('** OAuth Python Library Example **') client = SimpleOAuthClient(SERVER, PORT, REQUEST_TOKEN_URL, ACCESS_TOKEN_URL, AUTHORIZATION_URL) consumer = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET) signature_method_plaintext = oauth.OAuthSignatureMethod_PLAINTEXT() @@ -96,70 +96,70 @@ def run_example(): pause() # get request token - print '* Obtain a request token ...' + print('* Obtain a request token ...') pause() oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, callback=CALLBACK_URL, http_url=client.request_token_url) oauth_request.sign_request(signature_method_plaintext, consumer, None) - print 'REQUEST (via headers)' - print 'parameters: %s' % str(oauth_request.parameters) + print('REQUEST (via headers)') + print('parameters: %s' % str(oauth_request.parameters)) pause() token = client.fetch_request_token(oauth_request) - print 'GOT' - print 'key: %s' % str(token.key) - print 'secret: %s' % str(token.secret) - print 'callback confirmed? %s' % str(token.callback_confirmed) + print('GOT') + print('key: %s' % str(token.key)) + print('secret: %s' % str(token.secret)) + print('callback confirmed? %s' % str(token.callback_confirmed)) pause() - print '* Authorize the request token ...' + print('* Authorize the request token ...') pause() oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, http_url=client.authorization_url) - print 'REQUEST (via url query string)' - print 'parameters: %s' % str(oauth_request.parameters) + print('REQUEST (via url query string)') + print('parameters: %s' % str(oauth_request.parameters)) pause() # this will actually occur only on some callback response = client.authorize_token(oauth_request) - print 'GOT' - print response + print('GOT') + print(response) # sad way to get the verifier import urlparse, cgi query = urlparse.urlparse(response)[4] params = cgi.parse_qs(query, keep_blank_values=False) verifier = params['oauth_verifier'][0] - print 'verifier: %s' % verifier + print('verifier: %s' % verifier) pause() # get access token - print '* Obtain an access token ...' + print('* Obtain an access token ...') pause() oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, token=token, verifier=verifier, http_url=client.access_token_url) oauth_request.sign_request(signature_method_plaintext, consumer, token) - print 'REQUEST (via headers)' - print 'parameters: %s' % str(oauth_request.parameters) + print('REQUEST (via headers)') + print('parameters: %s' % str(oauth_request.parameters)) pause() token = client.fetch_access_token(oauth_request) - print 'GOT' - print 'key: %s' % str(token.key) - print 'secret: %s' % str(token.secret) + print('GOT') + print('key: %s' % str(token.key)) + print('secret: %s' % str(token.secret)) pause() # access some protected resources - print '* Access protected resources ...' + print('* Access protected resources ...') pause() parameters = {'file': 'vacation.jpg', 'size': 'original'} # resource specific params oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, token=token, http_method='POST', http_url=RESOURCE_URL, parameters=parameters) oauth_request.sign_request(signature_method_hmac_sha1, consumer, token) - print 'REQUEST (via post body)' - print 'parameters: %s' % str(oauth_request.parameters) + print('REQUEST (via post body)') + print('parameters: %s' % str(oauth_request.parameters)) pause() params = client.access_resource(oauth_request) - print 'GOT' - print 'non-oauth parameters: %s' % params + print('GOT') + print('non-oauth parameters: %s' % params) pause() def pause(): - print '' + print('') time.sleep(1) if __name__ == '__main__': run_example() - print 'Done.' \ No newline at end of file + print('Done.') diff --git a/example/server.py b/example/server.py index 5986b0e2..72fa2c47 100644 --- a/example/server.py +++ b/example/server.py @@ -186,10 +186,10 @@ def do_POST(self): def main(): try: server = HTTPServer(('', 8080), RequestHandler) - print 'Test server running...' + print('Test server running...') server.serve_forever() except KeyboardInterrupt: server.socket.close() if __name__ == '__main__': - main() \ No newline at end of file + main() diff --git a/setup.py b/setup.py index 98f42783..d6d29084 100755 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ if mo: mverstr = mo.group(1) else: - print "unable to find version in %s" % (VERSIONFILE,) + print("unable to find version in %s" % (VERSIONFILE,)) raise RuntimeError("if %s.py exists, it must be well-formed" % (VERSIONFILE,)) AVSRE = r"^auto_build_num *= *['\"]([^'\"]*)['\"]" mo = re.search(AVSRE, verstrline, re.M) From e058ec9bc276427ebe1d7dd10ee0a4142629fb14 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 28 May 2013 16:53:15 -0400 Subject: [PATCH 06/27] Drop Python 2.5 suport. The exception handling syntax problem is too hard. --- setup.py | 1 - tox.ini | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index d6d29084..ae37c148 100755 --- a/setup.py +++ b/setup.py @@ -34,7 +34,6 @@ classifiers=[ "Intended Audience :: Developers", "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.5", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: Implementation :: CPython", diff --git a/tox.ini b/tox.ini index 6e38d503..2f886e15 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = # py26,py27,py32,py33,pypy,cover,docs - py25,py26,py27,cover + py26,py27,cover [testenv] commands = From 463cb09b999094c532efd8636b3277d429b8aae6 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 29 May 2013 15:55:22 -0400 Subject: [PATCH 07/27] Py3: use 'except ... as' syntax. --- example/appengine_oauth.py | 4 ++-- example/server.py | 8 ++++---- oauth2/__init__.py | 8 ++++---- tests/test_oauth.py | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/example/appengine_oauth.py b/example/appengine_oauth.py index 814f9b6f..c14da764 100644 --- a/example/appengine_oauth.py +++ b/example/appengine_oauth.py @@ -86,7 +86,7 @@ def is_valid(self): request = self.get_oauth_request() client = self.get_client(request) params = self._server.verify_request(request, client, None) - except Exception, e: + except Exception as e: raise e return client @@ -95,7 +95,7 @@ class SampleHandler(OAuthHandler): def get(self): try: client = self.is_valid() - except Exception, e: + except Exception as e: self.error(500) self.response.out.write(e) diff --git a/example/server.py b/example/server.py index 72fa2c47..154e2596 100644 --- a/example/server.py +++ b/example/server.py @@ -131,7 +131,7 @@ def do_GET(self): self.end_headers() # return the token self.wfile.write(token.to_string()) - except oauth.OAuthError, err: + except oauth.OAuthError as err: self.send_oauth_error(err) return @@ -148,7 +148,7 @@ def do_GET(self): self.end_headers() # return the callback url (to show server has it) self.wfile.write(token.get_callback_url()) - except oauth.OAuthError, err: + except oauth.OAuthError as err: self.send_oauth_error(err) return @@ -162,7 +162,7 @@ def do_GET(self): self.end_headers() # return the token self.wfile.write(token.to_string()) - except oauth.OAuthError, err: + except oauth.OAuthError as err: self.send_oauth_error(err) return @@ -176,7 +176,7 @@ def do_GET(self): self.end_headers() # return the extra parameters - just for something to return self.wfile.write(str(params)) - except oauth.OAuthError, err: + except oauth.OAuthError as err: self.send_oauth_error(err) return diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 25d62a52..0223fcb2 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -102,7 +102,7 @@ def to_unicode(s): raise TypeError('You are required to pass either unicode or string here, not: %r (%s)' % (type(s), s)) try: s = s.decode('utf-8') - except UnicodeDecodeError, le: + except UnicodeDecodeError as le: raise TypeError('You are required to pass either a unicode object or a utf-8 string here. You passed a Python string object which contained non-utf-8: %r. The UnicodeDecodeError that resulted from attempting to interpret it as utf-8 was: %s' % (s, le,)) return s @@ -131,7 +131,7 @@ def to_unicode_optional_iterator(x): try: l = list(x) - except TypeError, e: + except TypeError as e: assert 'is not iterable' in str(e) return x else: @@ -147,7 +147,7 @@ def to_utf8_optional_iterator(x): try: l = list(x) - except TypeError, e: + except TypeError as e: assert 'is not iterable' in str(e) return x else: @@ -460,7 +460,7 @@ def get_normalized_parameters(self): else: try: value = list(value) - except TypeError, e: + except TypeError as e: assert 'is not iterable' in str(e) items.append((to_utf8_if_string(key), to_utf8_if_string(value))) else: diff --git a/tests/test_oauth.py b/tests/test_oauth.py index d6dd1370..07992af7 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -49,19 +49,19 @@ class TestError(unittest.TestCase): def test_message(self): try: raise oauth.Error - except oauth.Error, e: + except oauth.Error as e: self.assertEqual(e.message, 'OAuth error occurred.') msg = 'OMG THINGS BROKE!!!!' try: raise oauth.Error(msg) - except oauth.Error, e: + except oauth.Error as e: self.assertEqual(e.message, msg) def test_str(self): try: raise oauth.Error - except oauth.Error, e: + except oauth.Error as e: self.assertEquals(str(e), 'OAuth error occurred.') class TestGenerateFunctions(unittest.TestCase): From f81d1830b1c53b57205b908850a9ff5662faab11 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 29 May 2013 16:11:41 -0400 Subject: [PATCH 08/27] Tidy imports. - Sort per PEP8. - Remove Python 2.5 workaround. - Move sys.path munging to the 'if __name__ == __main__' stanza. --- tests/test_oauth.py | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 07992af7..8fdc6e05 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -23,26 +23,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -import sys -import os -import unittest -import oauth2 as oauth import random import time +import unittest import urllib import urlparse -from types import ListType -import mock -import httplib2 - -# Fix for python2.5 compatibility -try: - from urlparse import parse_qs, parse_qsl -except ImportError: - from cgi import parse_qs, parse_qsl +import httplib2 +import mock -sys.path[0:0] = [os.path.join(os.path.dirname(__file__), ".."),] +import oauth2 as oauth class TestError(unittest.TestCase): @@ -136,7 +126,7 @@ def test_basic(self): self.assertRaises(ValueError, lambda: oauth.Consumer(None, 'dasf')) def test_str(self): - res = dict(parse_qsl(str(self.consumer))) + res = dict(urlparse.parse_qsl(str(self.consumer))) self.assertTrue('oauth_consumer_key' in res) self.assertTrue('oauth_consumer_secret' in res) self.assertEquals(res['oauth_consumer_key'], self.consumer.key) @@ -495,7 +485,7 @@ def test_to_postdata(self): del params['multi'] flat.extend(params.items()) kf = lambda x: x[0] - self.assertEquals(sorted(flat, key=kf), sorted(parse_qsl(req.to_postdata()), key=kf)) + self.assertEquals(sorted(flat, key=kf), sorted(urlparse.parse_qsl(req.to_postdata()), key=kf)) def test_to_url(self): url = "http://sp.example.com/" @@ -517,8 +507,8 @@ def test_to_url(self): self.assertEquals(exp.netloc, res.netloc) self.assertEquals(exp.path, res.path) - a = parse_qs(exp.query) - b = parse_qs(res.query) + a = urlparse.parse_qs(exp.query) + b = urlparse.parse_qs(res.query) self.assertEquals(a, b) def test_to_url_with_query(self): @@ -542,8 +532,8 @@ def test_to_url_with_query(self): self.assertEquals(exp.netloc, res.netloc) self.assertEquals(exp.path, res.path) - a = parse_qs(exp.query) - b = parse_qs(res.query) + a = urlparse.parse_qs(exp.query) + b = urlparse.parse_qs(res.query) self.assertTrue('alt' in b) self.assertTrue('max-contacts' in b) self.assertEquals(b['alt'], ['json']) @@ -591,7 +581,7 @@ def test_signature_base_string_with_query(self): req = oauth.Request("GET", url, params) self.assertEquals(req.normalized_url, 'https://www.google.com/m8/feeds/contacts/default/full/') self.assertEquals(req.url, 'https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10') - normalized_params = parse_qsl(req.get_normalized_parameters()) + normalized_params = urlparse.parse_qsl(req.get_normalized_parameters()) self.assertTrue(len(normalized_params), len(params) + 2) normalized_params = dict(normalized_params) for key, value in params.iteritems(): @@ -928,7 +918,7 @@ def test_from_request(self): qs = urllib.urlencode(params) req = oauth.Request.from_request("GET", url, query_string=qs) - exp = parse_qs(qs, keep_blank_values=False) + exp = urlparse.parse_qs(qs, keep_blank_values=False) for k, v in exp.iteritems(): exp[k] = urllib.unquote(v[0]) @@ -1282,7 +1272,7 @@ def test_access_token_post(self): self.assertEquals(int(resp['status']), 200) - res = dict(parse_qsl(content)) + res = dict(urlparse.parse_qsl(content)) self.assertTrue('oauth_token' in res) self.assertTrue('oauth_token_secret' in res) @@ -1350,8 +1340,8 @@ def mockrequest(cl, ur, **kw): req = oauth.Request.from_consumer_and_token(self.consumer, None, http_method='GET', http_url=uri, parameters={}) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), self.consumer, None) - expected = parse_qsl(urlparse.urlparse(req.to_url()).query) - actual = parse_qsl(urlparse.urlparse(ur).query) + expected = urlparse.parse_qsl(urlparse.urlparse(req.to_url()).query) + actual = urlparse.parse_qsl(urlparse.urlparse(ur).query) self.failUnlessEqual(len(expected), len(actual)) actual = dict(actual) for key, value in expected: @@ -1381,4 +1371,7 @@ def test_multiple_values_for_a_key(self, mockReqConstructor, mockHttpRequest): self.failUnless('multi=2' in mockHttpRequest.call_args[1]['body']) if __name__ == "__main__": + import os + import sys + sys.path[0:0] = [os.path.join(os.path.dirname(__file__), ".."),] unittest.main() From 7d72ac16225a283f0fbf1d1ac6d1d7d20b3136c5 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 29 May 2013 16:38:55 -0400 Subject: [PATCH 09/27] PEP8: Wrap 80+ column lines. --- example/client.py | 42 ++++-- example/server.py | 22 +++- oauth2/__init__.py | 42 ++++-- setup.py | 3 +- tests/test_oauth.py | 302 +++++++++++++++++++++++++++++++------------- 5 files changed, 290 insertions(+), 121 deletions(-) diff --git a/example/client.py b/example/client.py index b050e05f..58524a9d 100644 --- a/example/client.py +++ b/example/client.py @@ -41,39 +41,45 @@ CALLBACK_URL = 'http://printer.example.com/request_token_ready' RESOURCE_URL = 'http://photos.example.net/photos' -# key and secret granted by the service provider for this consumer application - same as the MockOAuthDataStore +# key and secret granted by the service provider for this consumer +# application - same as the MockOAuthDataStore CONSUMER_KEY = 'key' CONSUMER_SECRET = 'secret' # example client using httplib with headers class SimpleOAuthClient(oauth.OAuthClient): - def __init__(self, server, port=httplib.HTTP_PORT, request_token_url='', access_token_url='', authorization_url=''): + def __init__(self, server, port=httplib.HTTP_PORT, request_token_url='', + access_token_url='', authorization_url=''): self.server = server self.port = port self.request_token_url = request_token_url self.access_token_url = access_token_url self.authorization_url = authorization_url - self.connection = httplib.HTTPConnection("%s:%d" % (self.server, self.port)) + self.connection = httplib.HTTPConnection( + "%s:%d" % (self.server, self.port)) def fetch_request_token(self, oauth_request): # via headers # -> OAuthToken - self.connection.request(oauth_request.http_method, self.request_token_url, headers=oauth_request.to_header()) + self.connection.request(oauth_request.http_method, + self.request_token_url, headers=oauth_request.to_header()) response = self.connection.getresponse() return oauth.OAuthToken.from_string(response.read()) def fetch_access_token(self, oauth_request): # via headers # -> OAuthToken - self.connection.request(oauth_request.http_method, self.access_token_url, headers=oauth_request.to_header()) + self.connection.request(oauth_request.http_method, + self.access_token_url, headers=oauth_request.to_header()) response = self.connection.getresponse() return oauth.OAuthToken.from_string(response.read()) def authorize_token(self, oauth_request): # via url # -> typically just some okay response - self.connection.request(oauth_request.http_method, oauth_request.to_url()) + self.connection.request(oauth_request.http_method, + oauth_request.to_url()) response = self.connection.getresponse() return response.read() @@ -81,7 +87,9 @@ def access_resource(self, oauth_request): # via post body # -> some protected resources headers = {'Content-Type' :'application/x-www-form-urlencoded'} - self.connection.request('POST', RESOURCE_URL, body=oauth_request.to_postdata(), headers=headers) + self.connection.request('POST', RESOURCE_URL, + body=oauth_request.to_postdata(), + headers=headers) response = self.connection.getresponse() return response.read() @@ -89,7 +97,8 @@ def run_example(): # setup print('** OAuth Python Library Example **') - client = SimpleOAuthClient(SERVER, PORT, REQUEST_TOKEN_URL, ACCESS_TOKEN_URL, AUTHORIZATION_URL) + client = SimpleOAuthClient(SERVER, PORT, REQUEST_TOKEN_URL, + ACCESS_TOKEN_URL, AUTHORIZATION_URL) consumer = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET) signature_method_plaintext = oauth.OAuthSignatureMethod_PLAINTEXT() signature_method_hmac_sha1 = oauth.OAuthSignatureMethod_HMAC_SHA1() @@ -98,7 +107,8 @@ def run_example(): # get request token print('* Obtain a request token ...') pause() - oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, callback=CALLBACK_URL, http_url=client.request_token_url) + oauth_request = oauth.OAuthRequest.from_consumer_and_token( + consumer, callback=CALLBACK_URL, http_url=client.request_token_url) oauth_request.sign_request(signature_method_plaintext, consumer, None) print('REQUEST (via headers)') print('parameters: %s' % str(oauth_request.parameters)) @@ -112,7 +122,8 @@ def run_example(): print('* Authorize the request token ...') pause() - oauth_request = oauth.OAuthRequest.from_token_and_callback(token=token, http_url=client.authorization_url) + oauth_request = oauth.OAuthRequest.from_token_and_callback( + token=token, http_url=client.authorization_url) print('REQUEST (via url query string)') print('parameters: %s' % str(oauth_request.parameters)) pause() @@ -131,7 +142,9 @@ def run_example(): # get access token print('* Obtain an access token ...') pause() - oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, token=token, verifier=verifier, http_url=client.access_token_url) + oauth_request = oauth.OAuthRequest.from_consumer_and_token( + consumer, token=token, verifier=verifier, + http_url=client.access_token_url) oauth_request.sign_request(signature_method_plaintext, consumer, token) print('REQUEST (via headers)') print('parameters: %s' % str(oauth_request.parameters)) @@ -145,8 +158,11 @@ def run_example(): # access some protected resources print('* Access protected resources ...') pause() - parameters = {'file': 'vacation.jpg', 'size': 'original'} # resource specific params - oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, token=token, http_method='POST', http_url=RESOURCE_URL, parameters=parameters) + parameters = {'file': 'vacation.jpg', + 'size': 'original'} # resource specific params + oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, + token=token, http_method='POST', http_url=RESOURCE_URL, + parameters=parameters) oauth_request.sign_request(signature_method_hmac_sha1, consumer, token) print('REQUEST (via post body)') print('parameters: %s' % str(oauth_request.parameters)) diff --git a/example/server.py b/example/server.py index 154e2596..e072f380 100644 --- a/example/server.py +++ b/example/server.py @@ -60,7 +60,11 @@ def lookup_token(self, token_type, token): return None def lookup_nonce(self, oauth_consumer, oauth_token, nonce): - if oauth_token and oauth_consumer.key == self.consumer.key and (oauth_token.key == self.request_token.key or oauth_token.key == self.access_token.key) and nonce == self.nonce: + if (oauth_token and + oauth_consumer.key == self.consumer.key and + (oauth_token.key == self.request_token.key or + oauth_token.key == self.access_token.key) and + nonce == self.nonce): return self.nonce return None @@ -74,7 +78,9 @@ def fetch_request_token(self, oauth_consumer, oauth_callback): return None def fetch_access_token(self, oauth_consumer, oauth_token, oauth_verifier): - if oauth_consumer.key == self.consumer.key and oauth_token.key == self.request_token.key and oauth_verifier == self.verifier: + if (oauth_consumer.key == self.consumer.key and + oauth_token.key == self.request_token.key and + oauth_verifier == self.verifier): # want to check here if token is authorized # for mock store, we assume it is return self.access_token @@ -91,8 +97,10 @@ class RequestHandler(BaseHTTPRequestHandler): def __init__(self, *args, **kwargs): self.oauth_server = oauth.OAuthServer(MockOAuthDataStore()) - self.oauth_server.add_signature_method(oauth.OAuthSignatureMethod_PLAINTEXT()) - self.oauth_server.add_signature_method(oauth.OAuthSignatureMethod_HMAC_SHA1()) + self.oauth_server.add_signature_method( + oauth.OAuthSignatureMethod_PLAINTEXT()) + self.oauth_server.add_signature_method( + oauth.OAuthSignatureMethod_HMAC_SHA1()) BaseHTTPRequestHandler.__init__(self, *args, **kwargs) # example way to send an oauth error @@ -119,7 +127,8 @@ def do_GET(self): pass # construct the oauth request from the request parameters - oauth_request = oauth.OAuthRequest.from_request(self.command, self.path, headers=self.headers, query_string=postdata) + oauth_request = oauth.OAuthRequest.from_request(self.command, + self.path, headers=self.headers, query_string=postdata) # request token if self.path.startswith(REQUEST_TOKEN_URL): @@ -170,7 +179,8 @@ def do_GET(self): if self.path.startswith(RESOURCE_URL): try: # verify the request has been oauth authorized - consumer, token, params = self.oauth_server.verify_request(oauth_request) + consumer, token, params = self.oauth_server.verify_request( + oauth_request) # send okay response self.send_response(200, 'OK') self.end_headers() diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 0223fcb2..1ba224fd 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -99,11 +99,17 @@ def to_unicode(s): message if s is not unicode, ascii, or utf-8. """ if not isinstance(s, unicode): if not isinstance(s, str): - raise TypeError('You are required to pass either unicode or string here, not: %r (%s)' % (type(s), s)) + raise TypeError('You are required to pass either unicode or ' + 'string here, not: %r (%s)' % (type(s), s)) try: s = s.decode('utf-8') except UnicodeDecodeError as le: - raise TypeError('You are required to pass either a unicode object or a utf-8 string here. You passed a Python string object which contained non-utf-8: %r. The UnicodeDecodeError that resulted from attempting to interpret it as utf-8 was: %s' % (s, le,)) + raise TypeError('You are required to pass either a unicode ' + 'object or a utf-8 string here. You passed a ' + 'Python string object which contained non-utf-8: ' + '%r. The UnicodeDecodeError that resulted from ' + 'attempting to interpret it as utf-8 was: %s' + % (s, le,)) return s def to_utf8(s): @@ -357,7 +363,8 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None, def url(self, value): self.__dict__['url'] = value if value is not None: - scheme, netloc, path, params, query, fragment = urlparse.urlparse(value) + scheme, netloc, path, params, query, fragment = urlparse.urlparse( + value) # Exclude default port numbers. if scheme == 'http' and netloc[-3:] == ':80': @@ -368,7 +375,8 @@ def url(self, value): raise ValueError("Unsupported URL %s (%s)." % (value, scheme)) # Normalized URL excludes params, query, and fragment. - self.normalized_url = urlparse.urlunparse((scheme, netloc, path, None, None, None)) + self.normalized_url = urlparse.urlunparse( + (scheme, netloc, path, None, None, None)) else: self.normalized_url = None self.__dict__['url'] = None @@ -462,15 +470,19 @@ def get_normalized_parameters(self): value = list(value) except TypeError as e: assert 'is not iterable' in str(e) - items.append((to_utf8_if_string(key), to_utf8_if_string(value))) + items.append( + (to_utf8_if_string(key), to_utf8_if_string(value))) else: - items.extend((to_utf8_if_string(key), to_utf8_if_string(item)) for item in value) + items.extend( + (to_utf8_if_string(key), to_utf8_if_string(item)) + for item in value) # Include any query string parameters from the provided URL query = urlparse.urlparse(self.url)[4] url_items = self._split_url_string(query).items() - url_items = [(to_utf8(k), to_utf8(v)) for k, v in url_items if k != 'oauth_signature' ] + url_items = [(to_utf8(k), to_utf8(v)) + for k, v in url_items if k != 'oauth_signature' ] items.extend(url_items) items.sort() @@ -486,7 +498,8 @@ def sign_request(self, signature_method, consumer, token): if not self.is_form_encoded: # according to - # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html + # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/ + # oauth-bodyhash.html # section 4.1.1 "OAuth Consumers MUST NOT include an # oauth_body_hash parameter on requests with form-encoded # request bodies." @@ -606,7 +619,8 @@ def _split_header(header): @staticmethod def _split_url_string(param_str): """Turn URL string into parameters.""" - parameters = parse_qs(param_str.encode('utf-8'), keep_blank_values=True) + parameters = parse_qs(param_str.encode('utf-8'), + keep_blank_values=True) for k, v in parameters.iteritems(): parameters[k] = urllib.unquote(v[0]) return parameters @@ -628,7 +642,8 @@ def __init__(self, consumer, token=None, cache=None, timeout=None, self.token = token self.method = SignatureMethod_HMAC_SHA1() - httplib2.Http.__init__(self, cache=cache, timeout=timeout, proxy_info=proxy_info) + httplib2.Http.__init__(self, cache=cache, timeout=timeout, + proxy_info=proxy_info) def set_signature_method(self, method): if not isinstance(method, SignatureMethod): @@ -734,7 +749,9 @@ def _get_signature_method(self, request): return self.signature_methods[signature_method] except KeyError: signature_method_names = ', '.join(self.signature_methods.keys()) - raise Error('Signature method %s not supported try one of the following: %s' % (signature_method, signature_method_names)) + raise Error('Signature method %s not supported try one of the ' + 'following: %s' + % (signature_method, signature_method_names)) def _check_signature(self, request, consumer, token): timestamp, nonce = request._get_timestamp_nonce() @@ -805,7 +822,8 @@ class SignatureMethod_HMAC_SHA1(SignatureMethod): name = 'HMAC-SHA1' def signing_base(self, request, consumer, token): - if not hasattr(request, 'normalized_url') or request.normalized_url is None: + if (not hasattr(request, 'normalized_url') or + request.normalized_url is None): raise ValueError("Base URL for request is not set.") sig = ( diff --git a/setup.py b/setup.py index ae37c148..66176b6f 100755 --- a/setup.py +++ b/setup.py @@ -16,7 +16,8 @@ mverstr = mo.group(1) else: print("unable to find version in %s" % (VERSIONFILE,)) - raise RuntimeError("if %s.py exists, it must be well-formed" % (VERSIONFILE,)) + raise RuntimeError("if %s.py exists, it must be well-formed" + % (VERSIONFILE,)) AVSRE = r"^auto_build_num *= *['\"]([^'\"]*)['\"]" mo = re.search(AVSRE, verstrline, re.M) if mo: diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 8fdc6e05..efdc32ed 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -161,7 +161,8 @@ def test_set_callback(self): self.assertEqual(self.token.callback, None) # TODO: The following test should probably not pass, but it does # To fix this, check for None and unset 'true' in set_callback - # Additionally, should a confirmation truly be done of the callback? + # Additionally, should a confirmation truly be done of the + # callback? self.assertEqual(self.token.callback_confirmed, 'true') def test_set_verifier(self): @@ -217,19 +218,34 @@ def _compare_tokens(self, new): def test_to_string(self): tok = oauth.Token('tooken', 'seecret') - self.assertEqual(str(tok), 'oauth_token_secret=seecret&oauth_token=tooken') + self.assertEqual(str(tok), + 'oauth_token_secret=seecret&oauth_token=tooken') def test_from_string(self): self.assertRaises(ValueError, lambda: oauth.Token.from_string('')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('blahblahblah')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('blah=blah')) - - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token_secret=asfdasf')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token_secret=')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=asfdasf')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=&oauth_token_secret=')) - self.assertRaises(ValueError, lambda: oauth.Token.from_string('oauth_token=tooken%26oauth_token_secret=seecret')) + self.assertRaises(ValueError, lambda: oauth.Token.from_string( + 'blahblahblah')) + self.assertRaises(ValueError, lambda: oauth.Token.from_string( + 'blah=blah')) + + self.assertRaises(ValueError, lambda: oauth.Token.from_string( + 'oauth_token_secret=asfdasf')) + self.assertRaises(ValueError, + lambda: oauth.Token.from_string( + 'oauth_token_secret=')) + self.assertRaises(ValueError, + lambda: oauth.Token.from_string( + 'oauth_token=asfdasf')) + self.assertRaises(ValueError, + lambda: oauth.Token.from_string( + 'oauth_token=')) + self.assertRaises(ValueError, + lambda: oauth.Token.from_string( + 'oauth_token=&oauth_token_secret=')) + self.assertRaises(ValueError, + lambda: oauth.Token.from_string( + 'oauth_token=tooken%26' + 'oauth_token_secret=seecret')) string = self.token.to_string() new = oauth.Token.from_string(string) @@ -243,14 +259,17 @@ def test_from_string(self): class ReallyEqualMixin: def failUnlessReallyEqual(self, a, b, msg=None): self.failUnlessEqual(a, b, msg=msg) - self.failUnlessEqual(type(a), type(b), msg="a :: %r, b :: %r, %r" % (a, b, msg)) + self.failUnlessEqual(type(a), type(b), + msg="a :: %r, b :: %r, %r" % (a, b, msg)) class TestFuncs(unittest.TestCase): def test_to_unicode(self): self.failUnlessRaises(TypeError, oauth.to_unicode, 0) self.failUnlessRaises(TypeError, oauth.to_unicode, '\xae') - self.failUnlessRaises(TypeError, oauth.to_unicode_optional_iterator, '\xae') - self.failUnlessRaises(TypeError, oauth.to_unicode_optional_iterator, ['\xae']) + self.failUnlessRaises(TypeError, + oauth.to_unicode_optional_iterator, '\xae') + self.failUnlessRaises(TypeError, + oauth.to_unicode_optional_iterator, ['\xae']) self.failUnlessEqual(oauth.to_unicode(':-)'), u':-)') self.failUnlessEqual(oauth.to_unicode(u'\u00ae'), u'\u00ae') @@ -365,21 +384,16 @@ def test_no_url_set(self): consumer = oauth.Consumer('my_consumer_key', 'my_consumer_secret') token = oauth.Token('my_key', 'my_secret') request = oauth.Request() - - try: - try: - request.sign_request(oauth.SignatureMethod_HMAC_SHA1(), - consumer, token) - except TypeError: - self.fail("Signature method didn't check for a normalized URL.") - except ValueError: - pass + self.assertRaises(ValueError, + request.sign_request, + oauth.SignatureMethod_HMAC_SHA1(), consumer, token) def test_url_query(self): - url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10" - normalized_url = urlparse.urlunparse(urlparse.urlparse(url)[:3] + (None, None, None)) + url = ("https://www.google.com/m8/feeds/contacts/default/full/" + "?alt=json&max-contacts=10") + normalized_url = urlparse.urlunparse( + urlparse.urlparse(url)[:3] + (None, None, None)) method = "GET" - req = oauth.Request(method, url) self.assertEquals(req.url, url) self.assertEquals(req.normalized_url, normalized_url) @@ -463,7 +477,16 @@ def test_to_postdata_nonascii(self): req = oauth.Request("GET", realm, params) - self.failUnlessReallyEqual(req.to_postdata(), 'nonasciithing=q%C2%BFu%C3%A9%20%2Caasp%20u%3F..a.s&oauth_nonce=4572616e48616d6d65724c61686176&oauth_timestamp=137131200&oauth_consumer_key=0685bd9184jfhq22&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&oauth_token=ad180jjd733klru7&oauth_signature=wOJIO9A2W5mFwDgiDvZbTSMK%252FPY%253D') + self.failUnlessReallyEqual( + req.to_postdata(), + ('nonasciithing=q%C2%BFu%C3%A9%20%2Caasp%20u%3F..a.s' + '&oauth_nonce=4572616e48616d6d65724c61686176' + '&oauth_timestamp=137131200' + '&oauth_consumer_key=0685bd9184jfhq22' + '&oauth_signature_method=HMAC-SHA1' + '&oauth_version=1.0' + '&oauth_token=ad180jjd733klru7' + '&oauth_signature=wOJIO9A2W5mFwDgiDvZbTSMK%252FPY%253D')) def test_to_postdata(self): realm = "http://sp.example.com/" @@ -485,7 +508,9 @@ def test_to_postdata(self): del params['multi'] flat.extend(params.items()) kf = lambda x: x[0] - self.assertEquals(sorted(flat, key=kf), sorted(urlparse.parse_qsl(req.to_postdata()), key=kf)) + self.assertEquals( + sorted(flat, key=kf), + sorted(urlparse.parse_qsl(req.to_postdata()), key=kf)) def test_to_url(self): url = "http://sp.example.com/" @@ -512,7 +537,8 @@ def test_to_url(self): self.assertEquals(a, b) def test_to_url_with_query(self): - url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10" + url = ("https://www.google.com/m8/feeds/contacts/default/full/" + "?alt=json&max-contacts=10") params = { 'oauth_version': "1.0", @@ -525,7 +551,8 @@ def test_to_url_with_query(self): } req = oauth.Request("GET", url, params) - # Note: the url above already has query parameters, so append new ones with & + # Note: the url above already has query parameters, so append new + # ones with & exp = urlparse.urlparse("%s&%s" % (url, urllib.urlencode(params))) res = urlparse.urlparse(req.to_url()) self.assertEquals(exp.scheme, res.scheme) @@ -543,32 +570,53 @@ def test_to_url_with_query(self): def test_signature_base_string_nonascii_nonutf8(self): consumer = oauth.Consumer('consumer_token', 'consumer_secret') - url = u'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc\u2766,+CA' + url = (u'http://api.simplegeo.com:80/1.0/places/address.json' + u'?q=monkeys&category=animal' + u'&address=41+Decatur+St,+San+Francisc\u2766,+CA') req = oauth.Request("GET", url) - self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') + self.failUnlessReallyEqual( + req.normalized_url, + u'http://api.simplegeo.com/1.0/places/address.json') req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + self.failUnlessReallyEqual( + req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') - url = 'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc\xe2\x9d\xa6,+CA' + url = ('http://api.simplegeo.com:80/1.0/places/address.json' + '?q=monkeys&category=animal' + '&address=41+Decatur+St,+San+Francisc\xe2\x9d\xa6,+CA') req = oauth.Request("GET", url) - self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') + self.failUnlessReallyEqual( + req.normalized_url, + u'http://api.simplegeo.com/1.0/places/address.json') req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + self.failUnlessReallyEqual( + req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') - url = 'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA' + url = ('http://api.simplegeo.com:80/1.0/places/address.json' + '?q=monkeys&category=animal' + '&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA') req = oauth.Request("GET", url) - self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') + self.failUnlessReallyEqual( + req.normalized_url, + u'http://api.simplegeo.com/1.0/places/address.json') req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + self.failUnlessReallyEqual( + req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') - url = u'http://api.simplegeo.com:80/1.0/places/address.json?q=monkeys&category=animal&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA' + url = (u'http://api.simplegeo.com:80/1.0/places/address.json' + u'?q=monkeys&category=animal' + u'&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA') req = oauth.Request("GET", url) - self.failUnlessReallyEqual(req.normalized_url, u'http://api.simplegeo.com/1.0/places/address.json') + self.failUnlessReallyEqual( + req.normalized_url, + u'http://api.simplegeo.com/1.0/places/address.json') req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + self.failUnlessReallyEqual( + req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') def test_signature_base_string_with_query(self): - url = "https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10" + url = ("https://www.google.com/m8/feeds/contacts/default/full/" + "?alt=json&max-contacts=10") params = { 'oauth_version': "1.0", 'oauth_nonce': "4572616e48616d6d65724c61686176", @@ -579,8 +627,10 @@ def test_signature_base_string_with_query(self): 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", } req = oauth.Request("GET", url, params) - self.assertEquals(req.normalized_url, 'https://www.google.com/m8/feeds/contacts/default/full/') - self.assertEquals(req.url, 'https://www.google.com/m8/feeds/contacts/default/full/?alt=json&max-contacts=10') + self.assertEquals( + req.normalized_url, + 'https://www.google.com/m8/feeds/contacts/default/full/') + self.assertEquals(req.url, bytes(url)) normalized_params = urlparse.parse_qsl(req.get_normalized_parameters()) self.assertTrue(len(normalized_params), len(params) + 2) normalized_params = dict(normalized_params) @@ -603,13 +653,20 @@ def test_get_normalized_parameters_empty(self): self.assertEquals(expected, res) def test_get_normalized_parameters_duplicate(self): - url = "http://example.com/v2/search/videos?oauth_nonce=79815175&oauth_timestamp=1295397962&oauth_consumer_key=mykey&oauth_signature_method=HMAC-SHA1&q=car&oauth_version=1.0&offset=10&oauth_signature=spWLI%2FGQjid7sQVd5%2FarahRxzJg%3D" + url = ("http://example.com/v2/search/videos" + "?oauth_nonce=79815175&oauth_timestamp=1295397962" + "&oauth_consumer_key=mykey&oauth_signature_method=HMAC-SHA1" + "&q=car&oauth_version=1.0&offset=10" + "&oauth_signature=spWLI%2FGQjid7sQVd5%2FarahRxzJg%3D") req = oauth.Request("GET", url) res = req.get_normalized_parameters() - expected='oauth_consumer_key=mykey&oauth_nonce=79815175&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1295397962&oauth_version=1.0&offset=10&q=car' + expected = ('oauth_consumer_key=mykey&oauth_nonce=79815175' + '&oauth_signature_method=HMAC-SHA1' + '&oauth_timestamp=1295397962&oauth_version=1.0' + '&offset=10&q=car') self.assertEquals(expected, res) @@ -618,13 +675,21 @@ def test_get_normalized_parameters_from_url(self): # https://github.com/ciaranj/node-oauth/blob/master/tests/oauth.js # which in turns says that it was copied from # http://oauth.net/core/1.0/#sig_base_example . - url = "http://photos.example.net/photos?file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original" + url = ("http://photos.example.net/photos?file=vacation.jpg" + "&oauth_consumer_key=dpf43f3p2l4k3l03" + "&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1" + "&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk" + "&oauth_version=1.0&size=original") req = oauth.Request("GET", url) res = req.get_normalized_parameters() - expected = 'file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original' + expected = ('file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03' + '&oauth_nonce=kllo9940pd9333jh' + '&oauth_signature_method=HMAC-SHA1' + '&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk' + '&oauth_version=1.0&size=original') self.assertEquals(expected, res) @@ -633,7 +698,11 @@ def test_signing_base(self): # https://github.com/ciaranj/node-oauth/blob/master/tests/oauth.js # which in turns says that it was copied from # http://oauth.net/core/1.0/#sig_base_example . - url = "http://photos.example.net/photos?file=vacation.jpg&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk&oauth_version=1.0&size=original" + url = ("http://photos.example.net/photos?file=vacation.jpg" + "&oauth_consumer_key=dpf43f3p2l4k3l03" + "&oauth_nonce=kllo9940pd9333jh&oauth_signature_method=HMAC-SHA1" + "&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk" + "&oauth_version=1.0&size=original") req = oauth.Request("GET", url) @@ -642,7 +711,14 @@ def test_signing_base(self): consumer = oauth.Consumer('dpf43f3p2l4k3l03', 'foo') key, raw = sm.signing_base(req, consumer, None) - expected = 'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal' + expected = ('GET&http%3A%2F%2Fphotos.example.net%2Fphotos' + '&file%3Dvacation.jpg' + '%26oauth_consumer_key%3Ddpf43f3p2l4k3l03' + '%26oauth_nonce%3Dkllo9940pd9333jh' + '%26oauth_signature_method%3DHMAC-SHA1' + '%26oauth_timestamp%3D1191242096' + '%26oauth_token%3Dnnch734d00sl2jdk' + '%26oauth_version%3D1.0%26size%3Doriginal') self.assertEquals(expected, raw) def test_get_normalized_parameters(self): @@ -665,7 +741,15 @@ def test_get_normalized_parameters(self): res = req.get_normalized_parameters() - expected='multi=BAR&multi=FOO&multi=%C2%AE&multi=%C2%AE&multi_same=FOO&multi_same=FOO&oauth_consumer_key=0685bd9184jfhq22&oauth_nonce=4572616e48616d6d65724c61686176&oauth_signature_method=HMAC-SHA1&oauth_timestamp=137131200&oauth_token=ad180jjd733klru7&oauth_version=1.0&uni_unicode_object=%C2%AE&uni_utf8_bytes=%C2%AE' + expected = ('multi=BAR&multi=FOO&multi=%C2%AE&multi=%C2%AE' + '&multi_same=FOO&multi_same=FOO' + '&oauth_consumer_key=0685bd9184jfhq22' + '&oauth_nonce=4572616e48616d6d65724c61686176' + '&oauth_signature_method=HMAC-SHA1' + '&oauth_timestamp=137131200' + '&oauth_token=ad180jjd733klru7' + '&oauth_version=1.0' + '&uni_unicode_object=%C2%AE&uni_utf8_bytes=%C2%AE') self.assertEquals(expected, res) @@ -678,7 +762,8 @@ def test_get_normalized_parameters_ignores_auth_signature(self): 'oauth_timestamp': "137131200", 'oauth_consumer_key': "0685bd9184jfhq22", 'oauth_signature_method': "HMAC-SHA1", - 'oauth_signature': "some-random-signature-%d" % random.randint(1000, 2000), + 'oauth_signature': "some-random-signature-%d" + % random.randint(1000, 2000), 'oauth_token': "ad180jjd733klru7", } @@ -713,7 +798,8 @@ def test_get_normalized_string_escapes_spaces_properly(self): url = "http://sp.example.com/" params = { "some_random_data": random.randint(100, 1000), - "data": "This data with a random number (%d) has spaces!" % random.randint(1000, 2000), + "data": "This data with a random number (%d) has spaces!" + % random.randint(1000, 2000), } req = oauth.Request("GET", url, params) @@ -740,20 +826,23 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): # If someone passes a sequence of bytes which is not ascii for # url, we'll raise an exception as early as possible. url = "http://sp.example.com/\x92" # It's actually cp1252-encoding... - self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) + self.assertRaises(TypeError, + oauth.Request, method="GET", url=url, parameters=params) # And if they pass an unicode, then we'll use it. url = u'http://sp.example.com/\u2019' req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'cMzvCkhvLL57+sTIxLITTHfkqZk=') + self.failUnlessReallyEqual( + req['oauth_signature'], 'cMzvCkhvLL57+sTIxLITTHfkqZk=') # And if it is a utf-8-encoded-then-percent-encoded non-ascii # thing, we'll decode it and use it. url = "http://sp.example.com/%E2%80%99" req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'yMLKOyNKC/DkyhUOb8DLSvceEWE=') + self.failUnlessReallyEqual( + req['oauth_signature'], 'yMLKOyNKC/DkyhUOb8DLSvceEWE=') # Same thing with the params. url = "http://sp.example.com/" @@ -761,32 +850,37 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): # If someone passes a sequence of bytes which is not ascii in # params, we'll raise an exception as early as possible. params['non_oauth_thing'] = '\xae', # It's actually cp1252-encoding... - self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) + self.assertRaises(TypeError, + oauth.Request, method="GET", url=url, parameters=params) # And if they pass a unicode, then we'll use it. params['non_oauth_thing'] = u'\u2019' req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_signature'], '0GU50m0v60CVDB5JnoBXnvvvKx4=') + self.failUnlessReallyEqual( + req['oauth_signature'], '0GU50m0v60CVDB5JnoBXnvvvKx4=') # And if it is a utf-8-encoded non-ascii thing, we'll decode # it and use it. params['non_oauth_thing'] = '\xc2\xae' req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_signature'], 'pqOCu4qvRTiGiXB8Z61Jsey0pMM=') + self.failUnlessReallyEqual( + req['oauth_signature'], 'pqOCu4qvRTiGiXB8Z61Jsey0pMM=') # Also if there are non-utf8 bytes in the query args. url = "http://sp.example.com/?q=\x92" # cp1252 - self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) + self.assertRaises(TypeError, + oauth.Request, method="GET", url=url, parameters=params) def test_request_hash_of_body(self): tok = oauth.Token(key="token", secret="tok-test-secret") con = oauth.Consumer(key="consumer", secret="con-test-secret") # Example 1a from Appendix A.1 of - # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html + # http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/ + # oauth-bodyhash.html # Except that we get a differetn result than they do. params = { @@ -798,10 +892,13 @@ def test_request_hash_of_body(self): } url = u"http://www.example.com/resource" - req = oauth.Request(method="PUT", url=url, parameters=params, body="Hello World!", is_form_encoded=False) + req = oauth.Request(method="PUT", url=url, parameters=params, + body="Hello World!", is_form_encoded=False) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_body_hash'], 'Lve95gjOVATpfV8EL5X4nxwjKHE=') - self.failUnlessReallyEqual(req['oauth_signature'], 't+MX8l/0S8hdbVQL99nD0X1fPnM=') + self.failUnlessReallyEqual( + req['oauth_body_hash'], 'Lve95gjOVATpfV8EL5X4nxwjKHE=') + self.failUnlessReallyEqual( + req['oauth_signature'], 't+MX8l/0S8hdbVQL99nD0X1fPnM=') # oauth-bodyhash.html A.1 has # '08bUFF%2Fjmp59mWB7cSgCYBUpJ0U%3D', but I don't see how that # is possible. @@ -815,10 +912,13 @@ def test_request_hash_of_body(self): 'oauth_consumer_key': con.key } - req = oauth.Request(method="PUT", url=url, parameters=params, body="Hello World!", is_form_encoded=False) + req = oauth.Request(method="PUT", url=url, parameters=params, + body="Hello World!", is_form_encoded=False) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_body_hash'], 'Lve95gjOVATpfV8EL5X4nxwjKHE=') - self.failUnlessReallyEqual(req['oauth_signature'], 'CTFmrqJIGT7NsWJ42OrujahTtTc=') + self.failUnlessReallyEqual( + req['oauth_body_hash'], 'Lve95gjOVATpfV8EL5X4nxwjKHE=') + self.failUnlessReallyEqual( + req['oauth_signature'], 'CTFmrqJIGT7NsWJ42OrujahTtTc=') # Appendix A.2 params = { @@ -829,10 +929,13 @@ def test_request_hash_of_body(self): 'oauth_consumer_key': con.key } - req = oauth.Request(method="GET", url=url, parameters=params, is_form_encoded=False) + req = oauth.Request(method="GET", url=url, parameters=params, + is_form_encoded=False) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual(req['oauth_body_hash'], '2jmj7l5rSw0yVb/vlWAYkK/YBwk=') - self.failUnlessReallyEqual(req['oauth_signature'], 'Zhl++aWSP0O3/hYQ0CuBc7jv38I=') + self.failUnlessReallyEqual( + req['oauth_body_hash'], '2jmj7l5rSw0yVb/vlWAYkK/YBwk=') + self.failUnlessReallyEqual( + req['oauth_signature'], 'Zhl++aWSP0O3/hYQ0CuBc7jv38I=') def test_sign_request(self): @@ -853,7 +956,8 @@ def test_sign_request(self): methods = { 'DX01TdHws7OninCLK9VztNTH1M4=': oauth.SignatureMethod_HMAC_SHA1(), - 'con-test-secret&tok-test-secret': oauth.SignatureMethod_PLAINTEXT() + 'con-test-secret&tok-test-secret': + oauth.SignatureMethod_PLAINTEXT() } for exp, method in methods.items(): @@ -865,23 +969,27 @@ def test_sign_request(self): url = "http://sp.example.com/\xe2\x80\x99" # utf-8 bytes req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') + self.assertEquals( + req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') url = u'http://sp.example.com/\u2019' # Python unicode object req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') + self.assertEquals( + req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') # Also if there are non-ascii chars in the query args. url = "http://sp.example.com/?q=\xe2\x80\x99" # utf-8 bytes req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') + self.assertEquals( + req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') url = u'http://sp.example.com/?q=\u2019' # Python unicode object req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals(req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') + self.assertEquals( + req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') def test_from_request(self): url = "http://sp.example.com/" @@ -998,7 +1106,8 @@ def setUp(self): self.request.sign_request(signature_method, self.consumer, self.token) def test_init(self): - server = oauth.Server(signature_methods={'HMAC-SHA1' : oauth.SignatureMethod_HMAC_SHA1()}) + server = oauth.Server( + signature_methods={'HMAC-SHA1' : oauth.SignatureMethod_HMAC_SHA1()}) self.assertTrue('HMAC-SHA1' in server.signature_methods) self.assertTrue(isinstance(server.signature_methods['HMAC-SHA1'], oauth.SignatureMethod_HMAC_SHA1)) @@ -1121,7 +1230,8 @@ def test_invalid_version(self): server = oauth.Server() server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1()) - self.assertRaises(oauth.Error, server.verify_request, request, consumer, token) + self.assertRaises(oauth.Error, + server.verify_request, request, consumer, token) def test_invalid_signature_method(self): url = "http://sp.example.com/" @@ -1308,18 +1418,23 @@ def test_multipart_post_does_not_alter_body(self, mockHttpRequest): def mockrequest(cl, ur, **kw): self.failUnless(cl is client) self.failUnless(ur is uri) - self.failUnlessEqual(frozenset(kw.keys()), frozenset(['method', 'body', 'redirections', 'connection_type', 'headers'])) + self.failUnlessEqual(frozenset(kw.keys()), + frozenset(['method', 'body', 'redirections', + 'connection_type', 'headers'])) self.failUnlessEqual(kw['body'], body) self.failUnlessEqual(kw['connection_type'], None) self.failUnlessEqual(kw['method'], 'POST') - self.failUnlessEqual(kw['redirections'], httplib2.DEFAULT_MAX_REDIRECTS) + self.failUnlessEqual(kw['redirections'], + httplib2.DEFAULT_MAX_REDIRECTS) self.failUnless(isinstance(kw['headers'], dict)) return random_result mockHttpRequest.side_effect = mockrequest - result = client.request(uri, 'POST', headers={'Content-Type':content_type}, body=body) + result = client.request(uri, 'POST', + headers={'Content-Type':content_type}, + body=body) self.assertEqual(result, random_result) @mock.patch('httplib2.Http.request') @@ -1330,22 +1445,28 @@ def test_url_with_query_string(self, mockHttpRequest): def mockrequest(cl, ur, **kw): self.failUnless(cl is client) - self.failUnlessEqual(frozenset(kw.keys()), frozenset(['method', 'body', 'redirections', 'connection_type', 'headers'])) + self.failUnlessEqual(frozenset(kw.keys()), + frozenset(['method', 'body', 'redirections', + 'connection_type', 'headers'])) self.failUnlessEqual(kw['body'], '') self.failUnlessEqual(kw['connection_type'], None) self.failUnlessEqual(kw['method'], 'GET') - self.failUnlessEqual(kw['redirections'], httplib2.DEFAULT_MAX_REDIRECTS) + self.failUnlessEqual(kw['redirections'], + httplib2.DEFAULT_MAX_REDIRECTS) self.failUnless(isinstance(kw['headers'], dict)) req = oauth.Request.from_consumer_and_token(self.consumer, None, http_method='GET', http_url=uri, parameters={}) - req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), self.consumer, None) - expected = urlparse.parse_qsl(urlparse.urlparse(req.to_url()).query) + req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), + self.consumer, None) + expected = urlparse.parse_qsl( + urlparse.urlparse(req.to_url()).query) actual = urlparse.parse_qsl(urlparse.urlparse(ur).query) self.failUnlessEqual(len(expected), len(actual)) actual = dict(actual) for key, value in expected: - if key not in ('oauth_signature', 'oauth_nonce', 'oauth_timestamp'): + if key not in ('oauth_signature', + 'oauth_nonce', 'oauth_timestamp'): self.failUnlessEqual(actual[key], value) return random_result @@ -1356,16 +1477,19 @@ def mockrequest(cl, ur, **kw): @mock.patch('httplib2.Http.request') @mock.patch('oauth2.Request.from_consumer_and_token') - def test_multiple_values_for_a_key(self, mockReqConstructor, mockHttpRequest): + def test_multiple_values_for_a_key(self, + mockReqConstructor, mockHttpRequest): client = oauth.Client(self.consumer, None) - request = oauth.Request("GET", "http://example.com/fetch.php", parameters={'multi': ['1', '2']}) + request = oauth.Request("GET", "http://example.com/fetch.php", + parameters={'multi': ['1', '2']}) mockReqConstructor.return_value = request client.request('http://whatever', 'POST', body='multi=1&multi=2') self.failUnlessEqual(mockReqConstructor.call_count, 1) - self.failUnlessEqual(mockReqConstructor.call_args[1]['parameters'], {'multi': ['1', '2']}) + self.failUnlessEqual(mockReqConstructor.call_args[1]['parameters'], + {'multi': ['1', '2']}) self.failUnless('multi=1' in mockHttpRequest.call_args[1]['body']) self.failUnless('multi=2' in mockHttpRequest.call_args[1]['body']) From e7d1be099861c3444c540717fcb76ac0df59743a Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 29 May 2013 16:42:06 -0400 Subject: [PATCH 10/27] Unshadow 'test_to_string'. --- tests/test_oauth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_oauth.py b/tests/test_oauth.py index efdc32ed..5d7f52c3 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -216,7 +216,7 @@ def _compare_tokens(self, new): # TODO: What about copying the verifier to the new token? # self.assertEqual(self.token.verifier, new.verifier) - def test_to_string(self): + def test___str__(self): tok = oauth.Token('tooken', 'seecret') self.assertEqual(str(tok), 'oauth_token_secret=seecret&oauth_token=tooken') From 9fc3e66809d97bdf1aea626a75d66400c5a21403 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 29 May 2013 16:45:10 -0400 Subject: [PATCH 11/27] Normalize --- tests/test_oauth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 5d7f52c3..cda7cdf1 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -891,7 +891,7 @@ def test_request_hash_of_body(self): 'oauth_consumer_key': con.key } - url = u"http://www.example.com/resource" + url = u'http://www.example.com/resource' req = oauth.Request(method="PUT", url=url, parameters=params, body="Hello World!", is_form_encoded=False) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) From f8c0ece9aab7bed06165be6496331e4bbb8c5d81 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 29 May 2013 17:00:17 -0400 Subject: [PATCH 12/27] Normalize away unicode literals. --- oauth2/_compat.py | 15 +++++ tests/test_oauth.py | 145 ++++++++++++++++++++++++++------------------ 2 files changed, 102 insertions(+), 58 deletions(-) create mode 100644 oauth2/_compat.py diff --git a/oauth2/_compat.py b/oauth2/_compat.py new file mode 100644 index 00000000..7bede17a --- /dev/null +++ b/oauth2/_compat.py @@ -0,0 +1,15 @@ +try: + TEXT = unicode +except NameError: #pragma NO COVER Py3k + TEXT = str + STRING_TYPES = (str, bytes) +else: + STRING_TYPES = (unicode, bytes) + +def u(x, encoding='ascii'): + if isinstance(x, TEXT): + return x + try: + return x.decode(encoding) + except AttributeError: + raise ValueError('WTF: %s' % x) diff --git a/tests/test_oauth.py b/tests/test_oauth.py index cda7cdf1..05571e68 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -34,6 +34,35 @@ import oauth2 as oauth +try: + unicode +except NameError: #pragma NO COVER Py3k + def u(x, encoding='ascii'): + if isinstance(x, str): + return x + try: + return x.decode(encoding) + except AttributeError: + return x + raise ValueError('WTF: %s' % x) +else: + def u(x, encoding='ascii'): + if isinstance(x, unicode): + return x + try: + return x.decode(encoding) + except AttributeError: + return x + raise ValueError('WTF: %s' % x) + +_UEMPTY = u('') +_UBLANK = u(' ') +_BSMILEY = b':-)' +_USMILEY = u(_BSMILEY) +_GLYPH = b'\xae' +_UGLYPH = u(_GLYPH, 'latin1') +_U2019 = u(b'\xe2\x80\x99', 'utf8') # u'\u2019' +_U2766 = u(b'\xe2\x9d\xa6', 'utf8') # u'\u2766' class TestError(unittest.TestCase): def test_message(self): @@ -80,7 +109,7 @@ def test_build_xoauth_string(self): parts = oauth_string.split(',') for part in parts: var, val = part.split('=') - returned[var] = val.strip('"') + returned[var] = val.strip('"') self.assertEquals('HMAC-SHA1', returned['oauth_signature_method']) self.assertEquals('user_token', returned['oauth_token']) @@ -271,51 +300,51 @@ def test_to_unicode(self): self.failUnlessRaises(TypeError, oauth.to_unicode_optional_iterator, ['\xae']) - self.failUnlessEqual(oauth.to_unicode(':-)'), u':-)') - self.failUnlessEqual(oauth.to_unicode(u'\u00ae'), u'\u00ae') - self.failUnlessEqual(oauth.to_unicode('\xc2\xae'), u'\u00ae') + self.failUnlessEqual(oauth.to_unicode(_BSMILEY), _USMILEY) + self.failUnlessEqual(oauth.to_unicode(_UGLYPH), _UGLYPH) + self.failUnlessEqual(oauth.to_unicode('\xc2\xae'), _UGLYPH) def test_to_utf8(self): self.failUnlessRaises(TypeError, oauth.to_utf8, 0) self.failUnlessRaises(TypeError, oauth.to_utf8, '\x81') - self.failUnlessEqual(oauth.to_utf8(':-)'), ':-)') - self.failUnlessEqual(oauth.to_utf8(u'\u00ae'), - u'\u00ae'.encode('utf8')) + self.failUnlessEqual(oauth.to_utf8(_BSMILEY), _BSMILEY) + self.failUnlessEqual(oauth.to_utf8(_UGLYPH), + _UGLYPH.encode('utf8')) def test_to_unicode_if_string(self): self.failUnless(oauth.to_unicode_if_string(self) is self) - self.failUnlessEqual(oauth.to_unicode_if_string(':-)'), u':-)') + self.failUnlessEqual(oauth.to_unicode_if_string(_BSMILEY), _USMILEY) def test_to_utf8_if_string(self): self.failUnless(oauth.to_utf8_if_string(self) is self) - self.failUnlessEqual(oauth.to_utf8_if_string(u':-)'), u':-)') - self.failUnlessEqual(oauth.to_utf8_if_string(u'\u00ae'), - u'\u00ae'.encode('utf8')) + self.failUnlessEqual(oauth.to_utf8_if_string(_USMILEY), _USMILEY) + self.failUnlessEqual(oauth.to_utf8_if_string(_UGLYPH), + _UGLYPH.encode('utf8')) def test_to_unicode_optional_iterator(self): - self.failUnlessEqual(oauth.to_unicode_optional_iterator(':-)'), - u':-)') - self.failUnlessEqual(oauth.to_unicode_optional_iterator(u'\u00ae'), - u'\u00ae') - self.failUnlessEqual(oauth.to_unicode_optional_iterator([':-)']), - [u':-)']) - self.failUnlessEqual(oauth.to_unicode_optional_iterator([u'\u00ae']), - [u'\u00ae']) - self.failUnlessEqual(oauth.to_unicode_optional_iterator((u'\u00ae',)), - [u'\u00ae']) + self.failUnlessEqual(oauth.to_unicode_optional_iterator(_BSMILEY), + _USMILEY) + self.failUnlessEqual(oauth.to_unicode_optional_iterator(_UGLYPH), + _UGLYPH) + self.failUnlessEqual(oauth.to_unicode_optional_iterator([_BSMILEY]), + [_USMILEY]) + self.failUnlessEqual(oauth.to_unicode_optional_iterator([_UGLYPH]), + [_UGLYPH]) + self.failUnlessEqual(oauth.to_unicode_optional_iterator((_UGLYPH,)), + [_UGLYPH]) self.failUnless(oauth.to_unicode_optional_iterator(self) is self) def test_to_utf8_optional_iterator(self): - self.failUnlessEqual(oauth.to_utf8_optional_iterator(':-)'), - ':-)') - self.failUnlessEqual(oauth.to_utf8_optional_iterator(u'\u00ae'), - u'\u00ae'.encode('utf8')) - self.failUnlessEqual(oauth.to_utf8_optional_iterator([':-)']), - [u':-)']) - self.failUnlessEqual(oauth.to_utf8_optional_iterator([u'\u00ae']), - [u'\u00ae'.encode('utf8')]) - self.failUnlessEqual(oauth.to_utf8_optional_iterator((u'\u00ae',)), - [u'\u00ae'.encode('utf8')]) + self.failUnlessEqual(oauth.to_utf8_optional_iterator(_BSMILEY), + _BSMILEY) + self.failUnlessEqual(oauth.to_utf8_optional_iterator(_UGLYPH), + _UGLYPH.encode('utf8')) + self.failUnlessEqual(oauth.to_utf8_optional_iterator([_BSMILEY]), + [_USMILEY]) + self.failUnlessEqual(oauth.to_utf8_optional_iterator([_UGLYPH]), + [_UGLYPH.encode('utf8')]) + self.failUnlessEqual(oauth.to_utf8_optional_iterator((_UGLYPH,)), + [_UGLYPH.encode('utf8')]) self.failUnless(oauth.to_utf8_optional_iterator(self) is self) class TestRequest(unittest.TestCase, ReallyEqualMixin): @@ -414,12 +443,12 @@ def test_get_nonoauth_parameters(self): } other_params = { - u'foo': u'baz', - u'bar': u'foo', - u'multi': [u'FOO',u'BAR'], - u'uni_utf8': u'\xae', - u'uni_unicode': u'\u00ae', - u'uni_unicode_2': u'åÅøØ', + u('foo'): u('baz'), + u('bar'): u('foo'), + u('multi'): [u('FOO'), u('BAR')], + u('uni_utf8'): u(b'\xae', 'latin1'), + u('uni_unicode'): _UGLYPH, + u('uni_unicode_2'): u(b'åÅøØ', 'latin1'), } params = oauth_params @@ -465,7 +494,7 @@ def test_to_postdata_nonascii(self): realm = "http://sp.example.com/" params = { - 'nonasciithing': u'q\xbfu\xe9 ,aasp u?..a.s', + 'nonasciithing': u('q\xbfu\xe9 ,aasp u?..a.s', 'latin1'), 'oauth_version': "1.0", 'oauth_nonce': "4572616e48616d6d65724c61686176", 'oauth_timestamp': "137131200", @@ -535,7 +564,7 @@ def test_to_url(self): a = urlparse.parse_qs(exp.query) b = urlparse.parse_qs(res.query) self.assertEquals(a, b) - + def test_to_url_with_query(self): url = ("https://www.google.com/m8/feeds/contacts/default/full/" "?alt=json&max-contacts=10") @@ -570,13 +599,13 @@ def test_to_url_with_query(self): def test_signature_base_string_nonascii_nonutf8(self): consumer = oauth.Consumer('consumer_token', 'consumer_secret') - url = (u'http://api.simplegeo.com:80/1.0/places/address.json' - u'?q=monkeys&category=animal' - u'&address=41+Decatur+St,+San+Francisc\u2766,+CA') + url = u('http://api.simplegeo.com:80/1.0/places/address.json' + '?q=monkeys&category=animal' + '&address=41+Decatur+St,+San+Francisc') + _U2766 + u(',+CA') req = oauth.Request("GET", url) self.failUnlessReallyEqual( req.normalized_url, - u'http://api.simplegeo.com/1.0/places/address.json') + u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) self.failUnlessReallyEqual( req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') @@ -587,7 +616,7 @@ def test_signature_base_string_nonascii_nonutf8(self): req = oauth.Request("GET", url) self.failUnlessReallyEqual( req.normalized_url, - u'http://api.simplegeo.com/1.0/places/address.json') + u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) self.failUnlessReallyEqual( req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') @@ -598,18 +627,18 @@ def test_signature_base_string_nonascii_nonutf8(self): req = oauth.Request("GET", url) self.failUnlessReallyEqual( req.normalized_url, - u'http://api.simplegeo.com/1.0/places/address.json') + u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) self.failUnlessReallyEqual( req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') - url = (u'http://api.simplegeo.com:80/1.0/places/address.json' - u'?q=monkeys&category=animal' - u'&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA') + url = u('http://api.simplegeo.com:80/1.0/places/address.json' + '?q=monkeys&category=animal' + '&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA') req = oauth.Request("GET", url) self.failUnlessReallyEqual( req.normalized_url, - u'http://api.simplegeo.com/1.0/places/address.json') + u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) self.failUnlessReallyEqual( req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') @@ -731,10 +760,10 @@ def test_get_normalized_parameters(self): 'oauth_consumer_key': "0685bd9184jfhq22", 'oauth_signature_method': "HMAC-SHA1", 'oauth_token': "ad180jjd733klru7", - 'multi': ['FOO','BAR', u'\u00ae', '\xc2\xae'], + 'multi': ['FOO','BAR', _UGLYPH, b'\xc2\xae'], 'multi_same': ['FOO','FOO'], - 'uni_utf8_bytes': '\xc2\xae', - 'uni_unicode_object': u'\u00ae' + 'uni_utf8_bytes': b'\xc2\xae', + 'uni_unicode_object': _UGLYPH } req = oauth.Request("GET", url, params) @@ -830,7 +859,7 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): oauth.Request, method="GET", url=url, parameters=params) # And if they pass an unicode, then we'll use it. - url = u'http://sp.example.com/\u2019' + url = u('http://sp.example.com/') + _U2019 req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) self.failUnlessReallyEqual( @@ -854,7 +883,7 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): oauth.Request, method="GET", url=url, parameters=params) # And if they pass a unicode, then we'll use it. - params['non_oauth_thing'] = u'\u2019' + params['non_oauth_thing'] = _U2019 req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) self.failUnlessReallyEqual( @@ -891,7 +920,7 @@ def test_request_hash_of_body(self): 'oauth_consumer_key': con.key } - url = u'http://www.example.com/resource' + url = u('http://www.example.com/resource') req = oauth.Request(method="PUT", url=url, parameters=params, body="Hello World!", is_form_encoded=False) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) @@ -972,7 +1001,7 @@ def test_sign_request(self): self.assertEquals( req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') - url = u'http://sp.example.com/\u2019' # Python unicode object + url = u('http://sp.example.com/') + _U2019 # Python unicode object req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) self.assertEquals( @@ -985,7 +1014,7 @@ def test_sign_request(self): self.assertEquals( req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') - url = u'http://sp.example.com/?q=\u2019' # Python unicode object + url = u('http://sp.example.com/?q=') + _U2019 # Python unicode object req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) self.assertEquals( @@ -1173,7 +1202,7 @@ def test_build_authenticate_header(self): server = oauth.Server() headers = server.build_authenticate_header('example.com') self.assertTrue('WWW-Authenticate' in headers) - self.assertEquals('OAuth realm="example.com"', + self.assertEquals('OAuth realm="example.com"', headers['WWW-Authenticate']) def test_no_version(self): From 517dbd846de504993091643eb988d18d5c3881d4 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 29 May 2013 18:20:56 -0400 Subject: [PATCH 13/27] Py3: compatible imports. Also, drop use of 'iteritems'. --- oauth2/__init__.py | 108 ++++++++++++++++++++------------------------ oauth2/_compat.py | 23 +++++++++- tests/test_oauth.py | 102 ++++++++++++++++++----------------------- 3 files changed, 116 insertions(+), 117 deletions(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 1ba224fd..acf878f7 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -23,31 +23,23 @@ """ import base64 -import urllib +from hashlib import sha1 import time import random -import urlparse import hmac import binascii import httplib2 -try: - from urlparse import parse_qs -except ImportError: #pragma NO COVER - # fall back for Python 2.5 - from cgi import parse_qs -else: #pragma NO COVER - parse_qs # placate pyflakes - -try: - from hashlib import sha1 as sha -except ImportError: #pragma NO COVER - # hashlib was added in Python 2.5 - import sha - -import _version - -__version__ = _version.__version__ +from ._compat import quote +from ._compat import STRING_TYPES +from ._compat import TEXT +from ._compat import unquote +from ._compat import unquote_to_bytes +from ._compat import urlencode +from ._compat import urlparse +from ._compat import urlunparse +from ._compat import parse_qs +from ._version import __version__ OAUTH_VERSION = '1.0' # Hi Blaine! HTTP_METHOD = 'GET' @@ -87,7 +79,7 @@ def build_xoauth_string(url, consumer, token=None): request.sign_request(signing_method, consumer, token) params = [] - for k, v in sorted(request.iteritems()): + for k, v in sorted(request.items()): if v is not None: params.append('%s="%s"' % (k, escape(v))) @@ -97,18 +89,19 @@ def build_xoauth_string(url, consumer, token=None): def to_unicode(s): """ Convert to unicode, raise exception with instructive error message if s is not unicode, ascii, or utf-8. """ - if not isinstance(s, unicode): - if not isinstance(s, str): + if not isinstance(s, TEXT): + if not isinstance(s, bytes): raise TypeError('You are required to pass either unicode or ' - 'string here, not: %r (%s)' % (type(s), s)) + 'bytes here, not: %r (%s)' % (type(s), s)) try: s = s.decode('utf-8') except UnicodeDecodeError as le: raise TypeError('You are required to pass either a unicode ' - 'object or a utf-8 string here. You passed a ' - 'Python string object which contained non-utf-8: ' - '%r. The UnicodeDecodeError that resulted from ' - 'attempting to interpret it as utf-8 was: %s' + 'object or a utf-8-enccoded bytes string here. ' + 'You passed a bytes object which contained ' + 'non-utf-8: %r. The UnicodeDecodeError that ' + 'resulted from attempting to interpret it as ' + 'utf-8 was: %s' % (s, le,)) return s @@ -116,13 +109,13 @@ def to_utf8(s): return to_unicode(s).encode('utf-8') def to_unicode_if_string(s): - if isinstance(s, basestring): + if isinstance(s, STRING_TYPES): return to_unicode(s) else: return s def to_utf8_if_string(s): - if isinstance(s, basestring): + if isinstance(s, STRING_TYPES): return to_utf8(s) else: return s @@ -132,7 +125,7 @@ def to_unicode_optional_iterator(x): Raise TypeError if x is a str containing non-utf8 bytes or if x is an iterable which contains such a str. """ - if isinstance(x, basestring): + if isinstance(x, STRING_TYPES): return to_unicode(x) try: @@ -148,7 +141,7 @@ def to_utf8_optional_iterator(x): Raise TypeError if x is a str or if x is an iterable which contains a str. """ - if isinstance(x, basestring): + if isinstance(x, STRING_TYPES): return to_utf8(x) try: @@ -161,7 +154,7 @@ def to_utf8_optional_iterator(x): def escape(s): """Escape a URL including any /.""" - return urllib.quote(s.encode('utf-8'), safe='~') + return quote(s.encode('utf-8'), safe='~') def generate_timestamp(): """Get seconds since epoch (UTC).""" @@ -212,7 +205,7 @@ def __str__(self): data = {'oauth_consumer_key': self.key, 'oauth_consumer_secret': self.secret} - return urllib.urlencode(data) + return urlencode(data) class Token(object): @@ -256,13 +249,13 @@ def set_verifier(self, verifier=None): def get_callback_url(self): if self.callback and self.verifier: # Append the oauth_verifier. - parts = urlparse.urlparse(self.callback) + parts = urlparse(self.callback) scheme, netloc, path, params, query, fragment = parts[:6] if query: query = '%s&oauth_verifier=%s' % (query, self.verifier) else: query = 'oauth_verifier=%s' % self.verifier - return urlparse.urlunparse((scheme, netloc, path, params, + return urlunparse((scheme, netloc, path, params, query, fragment)) return self.callback @@ -280,7 +273,7 @@ def to_string(self): if self.callback_confirmed is not None: data['oauth_callback_confirmed'] = self.callback_confirmed - return urllib.urlencode(data) + return urlencode(data) @staticmethod def from_string(s): @@ -351,7 +344,7 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None, self.url = to_unicode(url) self.method = method if parameters is not None: - for k, v in parameters.iteritems(): + for k, v in parameters.items(): k = to_unicode(k) v = to_unicode_optional_iterator(v) self[k] = v @@ -363,8 +356,7 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None, def url(self, value): self.__dict__['url'] = value if value is not None: - scheme, netloc, path, params, query, fragment = urlparse.urlparse( - value) + scheme, netloc, path, params, query, fragment = urlparse(value) # Exclude default port numbers. if scheme == 'http' and netloc[-3:] == ':80': @@ -375,7 +367,7 @@ def url(self, value): raise ValueError("Unsupported URL %s (%s)." % (value, scheme)) # Normalized URL excludes params, query, and fragment. - self.normalized_url = urlparse.urlunparse( + self.normalized_url = urlunparse( (scheme, netloc, path, None, None, None)) else: self.normalized_url = None @@ -390,7 +382,7 @@ def _get_timestamp_nonce(self): def get_nonoauth_parameters(self): """Get any non-OAuth parameters.""" - return dict([(k, v) for k, v in self.iteritems() + return dict([(k, v) for k, v in self.items() if not k.startswith('oauth_')]) def to_header(self, realm=''): @@ -410,17 +402,17 @@ def to_header(self, realm=''): def to_postdata(self): """Serialize as post data for a POST request.""" d = {} - for k, v in self.iteritems(): + for k, v in self.items(): d[k.encode('utf-8')] = to_utf8_optional_iterator(v) # tell urlencode to deal with sequence values and map them correctly # to resulting querystring. for example self["k"] = ["v1", "v2"] will # result in 'k=v1&k=v2' and not k=%5B%27v1%27%2C+%27v2%27%5D - return urllib.urlencode(d, True).replace('+', '%20') + return urlencode(d, True).replace('+', '%20') def to_url(self): """Serialize as a URL for a GET request.""" - base_url = urlparse.urlparse(self.url) + base_url = urlparse(self.url) try: query = base_url.query except AttributeError: #pragma NO COVER @@ -445,8 +437,8 @@ def to_url(self): fragment = base_url[5] url = (scheme, netloc, path, params, - urllib.urlencode(query, True), fragment) - return urlparse.urlunparse(url) + urlencode(query, True), fragment) + return urlunparse(url) def get_parameter(self, parameter): ret = self.get(parameter) @@ -458,12 +450,12 @@ def get_parameter(self, parameter): def get_normalized_parameters(self): """Return a string that contains the parameters that must be signed.""" items = [] - for key, value in self.iteritems(): + for key, value in self.items(): if key == 'oauth_signature': continue # 1.0a/9.1.1 states that kvp must be sorted by key, then by value, # so we unpack sequence values into multiple items for sorting. - if isinstance(value, basestring): + if isinstance(value, STRING_TYPES): items.append((to_utf8_if_string(key), to_utf8(value))) else: try: @@ -478,7 +470,7 @@ def get_normalized_parameters(self): for item in value) # Include any query string parameters from the provided URL - query = urlparse.urlparse(self.url)[4] + query = urlparse(self.url)[4] url_items = self._split_url_string(query).items() url_items = [(to_utf8(k), to_utf8(v)) @@ -486,7 +478,7 @@ def get_normalized_parameters(self): items.extend(url_items) items.sort() - encoded_str = urllib.urlencode(items) + encoded_str = urlencode(items) # Encode signature parameters per Oauth Core 1.0 protocol # spec draft 7, section 3.6 # (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6) @@ -503,7 +495,7 @@ def sign_request(self, signature_method, consumer, token): # section 4.1.1 "OAuth Consumers MUST NOT include an # oauth_body_hash parameter on requests with form-encoded # request bodies." - self['oauth_body_hash'] = base64.b64encode(sha(self.body).digest()) + self['oauth_body_hash'] = base64.b64encode(sha1(self.body).digest()) if 'oauth_consumer_key' not in self: self['oauth_consumer_key'] = consumer.key @@ -551,7 +543,7 @@ def from_request(cls, http_method, http_url, headers=None, parameters=None, parameters.update(query_params) # URL parameters. - param_str = urlparse.urlparse(http_url)[4] # query + param_str = urlparse(http_url)[4] # query url_params = cls._split_url_string(param_str) parameters.update(url_params) @@ -613,7 +605,7 @@ def _split_header(header): # Split key-value. param_parts = param.split('=', 1) # Remove quotes and unescape the value. - params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) + params[param_parts[0]] = unquote(param_parts[1].strip('\"')) return params @staticmethod @@ -621,8 +613,8 @@ def _split_url_string(param_str): """Turn URL string into parameters.""" parameters = parse_qs(param_str.encode('utf-8'), keep_blank_values=True) - for k, v in parameters.iteritems(): - parameters[k] = urllib.unquote(v[0]) + for k, v in parameters.items(): + parameters[k] = unquote_to_bytes(v[0]) return parameters @@ -676,8 +668,8 @@ def request(self, uri, method="GET", body='', headers=None, req.sign_request(self.method, self.consumer, self.token) - scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri) - realm = urlparse.urlunparse((scheme, netloc, '', None, None, None)) + scheme, netloc, path, params, query, fragment = urlparse(uri) + realm = urlunparse((scheme, netloc, '', None, None, None)) if is_form_encoded: body = req.to_postdata() @@ -842,7 +834,7 @@ def sign(self, request, consumer, token): """Builds the base signature string.""" key, raw = self.signing_base(request, consumer, token) - hashed = hmac.new(key, raw, sha) + hashed = hmac.new(key, raw, sha1) # Calculate the digest base 64. return binascii.b2a_base64(hashed.digest())[:-1] diff --git a/oauth2/_compat.py b/oauth2/_compat.py index 7bede17a..067a9015 100644 --- a/oauth2/_compat.py +++ b/oauth2/_compat.py @@ -3,7 +3,7 @@ except NameError: #pragma NO COVER Py3k TEXT = str STRING_TYPES = (str, bytes) -else: +else: #pragma NO COVER Python2 STRING_TYPES = (unicode, bytes) def u(x, encoding='ascii'): @@ -13,3 +13,24 @@ def u(x, encoding='ascii'): return x.decode(encoding) except AttributeError: raise ValueError('WTF: %s' % x) + +try: + import urlparse +except ImportError: #pragma NO COVER Py3k + from urllib.parse import parse_qs + from urllib.parse import parse_qsl + from urllib.parse import quote + from urllib.parse import unquote + from urllib.parse import unquote_to_bytes + from urllib.parse import urlencode + from urllib.parse import urlparse + from urllib.parse import urlunparse +else: #pragma NO COVER Python2 + from urlparse import parse_qs + from urlparse import parse_qsl + from urllib import quote + from urllib import unquote + from urllib import urlencode + from urlparse import urlparse + from urlparse import urlunparse + unquote_to_bytes = unquote diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 05571e68..82514fe7 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -26,34 +26,19 @@ import random import time import unittest -import urllib -import urlparse import httplib2 import mock -import oauth2 as oauth +from oauth2._compat import unquote +from oauth2._compat import urlencode +from oauth2._compat import urlparse +from oauth2._compat import urlunparse +from oauth2._compat import parse_qs +from oauth2._compat import parse_qsl +from oauth2._compat import u -try: - unicode -except NameError: #pragma NO COVER Py3k - def u(x, encoding='ascii'): - if isinstance(x, str): - return x - try: - return x.decode(encoding) - except AttributeError: - return x - raise ValueError('WTF: %s' % x) -else: - def u(x, encoding='ascii'): - if isinstance(x, unicode): - return x - try: - return x.decode(encoding) - except AttributeError: - return x - raise ValueError('WTF: %s' % x) +import oauth2 as oauth _UEMPTY = u('') _UBLANK = u(' ') @@ -155,7 +140,7 @@ def test_basic(self): self.assertRaises(ValueError, lambda: oauth.Consumer(None, 'dasf')) def test_str(self): - res = dict(urlparse.parse_qsl(str(self.consumer))) + res = dict(parse_qsl(str(self.consumer))) self.assertTrue('oauth_consumer_key' in res) self.assertTrue('oauth_consumer_secret' in res) self.assertEquals(res['oauth_consumer_key'], self.consumer.key) @@ -294,19 +279,19 @@ def failUnlessReallyEqual(self, a, b, msg=None): class TestFuncs(unittest.TestCase): def test_to_unicode(self): self.failUnlessRaises(TypeError, oauth.to_unicode, 0) - self.failUnlessRaises(TypeError, oauth.to_unicode, '\xae') + self.failUnlessRaises(TypeError, oauth.to_unicode, b'\xae') self.failUnlessRaises(TypeError, - oauth.to_unicode_optional_iterator, '\xae') + oauth.to_unicode_optional_iterator, b'\xae') self.failUnlessRaises(TypeError, - oauth.to_unicode_optional_iterator, ['\xae']) + oauth.to_unicode_optional_iterator, [b'\xae']) self.failUnlessEqual(oauth.to_unicode(_BSMILEY), _USMILEY) self.failUnlessEqual(oauth.to_unicode(_UGLYPH), _UGLYPH) - self.failUnlessEqual(oauth.to_unicode('\xc2\xae'), _UGLYPH) + self.failUnlessEqual(oauth.to_unicode(b'\xc2\xae'), _UGLYPH) def test_to_utf8(self): self.failUnlessRaises(TypeError, oauth.to_utf8, 0) - self.failUnlessRaises(TypeError, oauth.to_utf8, '\x81') + self.failUnlessRaises(TypeError, oauth.to_utf8, b'\x81') self.failUnlessEqual(oauth.to_utf8(_BSMILEY), _BSMILEY) self.failUnlessEqual(oauth.to_utf8(_UGLYPH), _UGLYPH.encode('utf8')) @@ -317,7 +302,7 @@ def test_to_unicode_if_string(self): def test_to_utf8_if_string(self): self.failUnless(oauth.to_utf8_if_string(self) is self) - self.failUnlessEqual(oauth.to_utf8_if_string(_USMILEY), _USMILEY) + self.failUnlessEqual(oauth.to_utf8_if_string(_USMILEY), _BSMILEY) self.failUnlessEqual(oauth.to_utf8_if_string(_UGLYPH), _UGLYPH.encode('utf8')) @@ -340,7 +325,9 @@ def test_to_utf8_optional_iterator(self): self.failUnlessEqual(oauth.to_utf8_optional_iterator(_UGLYPH), _UGLYPH.encode('utf8')) self.failUnlessEqual(oauth.to_utf8_optional_iterator([_BSMILEY]), - [_USMILEY]) + [_BSMILEY]) + self.failUnlessEqual(oauth.to_utf8_optional_iterator([_USMILEY]), + [_BSMILEY]) self.failUnlessEqual(oauth.to_utf8_optional_iterator([_UGLYPH]), [_UGLYPH.encode('utf8')]) self.failUnlessEqual(oauth.to_utf8_optional_iterator((_UGLYPH,)), @@ -420,8 +407,7 @@ def test_no_url_set(self): def test_url_query(self): url = ("https://www.google.com/m8/feeds/contacts/default/full/" "?alt=json&max-contacts=10") - normalized_url = urlparse.urlunparse( - urlparse.urlparse(url)[:3] + (None, None, None)) + normalized_url = urlunparse(urlparse(url)[:3] + (None, None, None)) method = "GET" req = oauth.Request(method, url) self.assertEquals(req.url, url) @@ -480,7 +466,7 @@ def test_to_header(self): res = {} for v in vars: var, val = v.split('=') - res[var] = urllib.unquote(val.strip('"')) + res[var] = unquote(val.strip('"')) self.assertEquals(realm, res['realm']) del res['realm'] @@ -539,7 +525,7 @@ def test_to_postdata(self): kf = lambda x: x[0] self.assertEquals( sorted(flat, key=kf), - sorted(urlparse.parse_qsl(req.to_postdata()), key=kf)) + sorted(parse_qsl(req.to_postdata()), key=kf)) def test_to_url(self): url = "http://sp.example.com/" @@ -555,14 +541,14 @@ def test_to_url(self): } req = oauth.Request("GET", url, params) - exp = urlparse.urlparse("%s?%s" % (url, urllib.urlencode(params))) - res = urlparse.urlparse(req.to_url()) + exp = urlparse("%s?%s" % (url, urlencode(params))) + res = urlparse(req.to_url()) self.assertEquals(exp.scheme, res.scheme) self.assertEquals(exp.netloc, res.netloc) self.assertEquals(exp.path, res.path) - a = urlparse.parse_qs(exp.query) - b = urlparse.parse_qs(res.query) + a = parse_qs(exp.query) + b = parse_qs(res.query) self.assertEquals(a, b) def test_to_url_with_query(self): @@ -582,14 +568,14 @@ def test_to_url_with_query(self): req = oauth.Request("GET", url, params) # Note: the url above already has query parameters, so append new # ones with & - exp = urlparse.urlparse("%s&%s" % (url, urllib.urlencode(params))) - res = urlparse.urlparse(req.to_url()) + exp = urlparse("%s&%s" % (url, urlencode(params))) + res = urlparse(req.to_url()) self.assertEquals(exp.scheme, res.scheme) self.assertEquals(exp.netloc, res.netloc) self.assertEquals(exp.path, res.path) - a = urlparse.parse_qs(exp.query) - b = urlparse.parse_qs(res.query) + a = parse_qs(exp.query) + b = parse_qs(res.query) self.assertTrue('alt' in b) self.assertTrue('max-contacts' in b) self.assertEquals(b['alt'], ['json']) @@ -660,10 +646,10 @@ def test_signature_base_string_with_query(self): req.normalized_url, 'https://www.google.com/m8/feeds/contacts/default/full/') self.assertEquals(req.url, bytes(url)) - normalized_params = urlparse.parse_qsl(req.get_normalized_parameters()) + normalized_params = parse_qsl(req.get_normalized_parameters()) self.assertTrue(len(normalized_params), len(params) + 2) normalized_params = dict(normalized_params) - for key, value in params.iteritems(): + for key, value in params.items(): if key == 'oauth_signature': continue self.assertEquals(value, normalized_params[key]) @@ -800,11 +786,11 @@ def test_get_normalized_parameters_ignores_auth_signature(self): res = req.get_normalized_parameters() - self.assertNotEquals(urllib.urlencode(sorted(params.items())), res) + self.assertNotEquals(urlencode(sorted(params.items())), res) foo = params.copy() del foo["oauth_signature"] - self.assertEqual(urllib.urlencode(sorted(foo.items())), res) + self.assertEqual(urlencode(sorted(foo.items())), res) def test_set_signature_method(self): consumer = oauth.Consumer('key', 'secret') @@ -833,7 +819,7 @@ def test_get_normalized_string_escapes_spaces_properly(self): req = oauth.Request("GET", url, params) res = req.get_normalized_parameters() - expected = urllib.urlencode(sorted(params.items())).replace('+', '%20') + expected = urlencode(sorted(params.items())).replace('+', '%20') self.assertEqual(expected, res) @mock.patch('oauth2.Request.make_timestamp') @@ -1052,12 +1038,12 @@ def test_from_request(self): url, bad_headers) # Test getting from query string - qs = urllib.urlencode(params) + qs = urlencode(params) req = oauth.Request.from_request("GET", url, query_string=qs) - exp = urlparse.parse_qs(qs, keep_blank_values=False) - for k, v in exp.iteritems(): - exp[k] = urllib.unquote(v[0]) + exp = parse_qs(qs, keep_blank_values=False) + for k, v in exp.items(): + exp[k] = unquote(v[0]) self.assertEquals(exp, req.copy()) @@ -1369,7 +1355,7 @@ def create_simple_multipart_data(self, data): boundary = '---Boundary-%d' % random.randint(1,1000) crlf = '\r\n' items = [] - for key, value in data.iteritems(): + for key, value in data.items(): items += [ '--'+boundary, 'Content-Disposition: form-data; name="%s"'%str(key), @@ -1411,7 +1397,7 @@ def test_access_token_post(self): self.assertEquals(int(resp['status']), 200) - res = dict(urlparse.parse_qsl(content)) + res = dict(parse_qsl(content)) self.assertTrue('oauth_token' in res) self.assertTrue('oauth_token_secret' in res) @@ -1419,7 +1405,7 @@ def _two_legged(self, method): client = oauth.Client(self.consumer, None) return client.request(self._uri('two_legged'), method, - body=urllib.urlencode(self.body)) + body=urlencode(self.body)) def test_two_legged_post(self): """A test of a two-legged OAuth POST request.""" @@ -1488,9 +1474,9 @@ def mockrequest(cl, ur, **kw): http_method='GET', http_url=uri, parameters={}) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), self.consumer, None) - expected = urlparse.parse_qsl( - urlparse.urlparse(req.to_url()).query) - actual = urlparse.parse_qsl(urlparse.urlparse(ur).query) + expected = parse_qsl( + urlparse(req.to_url()).query) + actual = parse_qsl(urlparse(ur).query) self.failUnlessEqual(len(expected), len(actual)) actual = dict(actual) for key, value in expected: From 69cb2b9e35f4390675cd7b09efa8c812e10d090f Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 29 May 2013 18:48:58 -0400 Subject: [PATCH 14/27] Don't use non-ASCII bytes literals. --- tests/test_oauth.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 82514fe7..9dcc7305 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -434,7 +434,8 @@ def test_get_nonoauth_parameters(self): u('multi'): [u('FOO'), u('BAR')], u('uni_utf8'): u(b'\xae', 'latin1'), u('uni_unicode'): _UGLYPH, - u('uni_unicode_2'): u(b'åÅøØ', 'latin1'), + u('uni_unicode_2'): + u(b'\xc3\xa5\xc3\x85\xc3\xb8\xc3\x98', 'latin1'), # 'åÅøØ' } params = oauth_params From 17880b6019ce0d2739b5740ee61364684169bd6d Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 29 May 2013 18:56:54 -0400 Subject: [PATCH 15/27] Restore coverage. --- oauth2/_compat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oauth2/_compat.py b/oauth2/_compat.py index 067a9015..65036dda 100644 --- a/oauth2/_compat.py +++ b/oauth2/_compat.py @@ -7,11 +7,11 @@ STRING_TYPES = (unicode, bytes) def u(x, encoding='ascii'): - if isinstance(x, TEXT): + if isinstance(x, TEXT): #pragma NO COVER return x try: return x.decode(encoding) - except AttributeError: + except AttributeError: #pragma NO COVER raise ValueError('WTF: %s' % x) try: From 3d5d52f7d529183bd56da43df2503a53fe3b6fc8 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 4 Jun 2013 16:24:11 -0400 Subject: [PATCH 16/27] Add a 'b()' utility for forcing encoding to bytes. In Python2, the 'bytes()' builtin doesn't take an encoding argument. --- oauth2/_compat.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/oauth2/_compat.py b/oauth2/_compat.py index 65036dda..ed77b391 100644 --- a/oauth2/_compat.py +++ b/oauth2/_compat.py @@ -3,8 +3,14 @@ except NameError: #pragma NO COVER Py3k TEXT = str STRING_TYPES = (str, bytes) + def b(x, encoding='ascii'): + return bytes(x, encoding) else: #pragma NO COVER Python2 STRING_TYPES = (unicode, bytes) + def b(x, encoding='ascii'): + if isinstance(x, unicode): + x = x.encode(encoding) + return x def u(x, encoding='ascii'): if isinstance(x, TEXT): #pragma NO COVER From da9145c54919f27a13217fd2db225366ecdf6925 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Tue, 4 Jun 2013 19:37:51 -0400 Subject: [PATCH 17/27] Moar Py3k compat. Tests all now pass on Py3k, with two remaining failures on Python2. --- oauth2/__init__.py | 21 ++++--- oauth2/_compat.py | 2 + tests/test_oauth.py | 138 ++++++++++++++++++++++++-------------------- 3 files changed, 90 insertions(+), 71 deletions(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index acf878f7..342b9204 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -30,6 +30,8 @@ import binascii import httplib2 +from ._compat import PY3 +from ._compat import parse_qs from ._compat import quote from ._compat import STRING_TYPES from ._compat import TEXT @@ -38,7 +40,6 @@ from ._compat import urlencode from ._compat import urlparse from ._compat import urlunparse -from ._compat import parse_qs from ._version import __version__ OAUTH_VERSION = '1.0' # Hi Blaine! @@ -154,7 +155,9 @@ def to_utf8_optional_iterator(x): def escape(s): """Escape a URL including any /.""" - return quote(s.encode('utf-8'), safe='~') + if not isinstance(s, bytes): + s = s.encode('utf-8') + return quote(s, safe='~') def generate_timestamp(): """Get seconds since epoch (UTC).""" @@ -339,7 +342,7 @@ class Request(dict): version = OAUTH_VERSION def __init__(self, method=HTTP_METHOD, url=None, parameters=None, - body='', is_form_encoded=False): + body=b'', is_form_encoded=False): if url is not None: self.url = to_unicode(url) self.method = method @@ -555,7 +558,7 @@ def from_request(cls, http_method, http_url, headers=None, parameters=None, @classmethod def from_consumer_and_token(cls, consumer, token=None, http_method=HTTP_METHOD, http_url=None, parameters=None, - body='', is_form_encoded=False): + body=b'', is_form_encoded=False): if not parameters: parameters = {} @@ -611,7 +614,9 @@ def _split_header(header): @staticmethod def _split_url_string(param_str): """Turn URL string into parameters.""" - parameters = parse_qs(param_str.encode('utf-8'), + #XXX parse_qs is leaving the encoded bytes after un-escaping + #parameters = parse_qs(param_str.encode('utf-8'), + parameters = parse_qs(param_str, keep_blank_values=True) for k, v in parameters.items(): parameters[k] = unquote_to_bytes(v[0]) @@ -643,7 +648,7 @@ def set_signature_method(self, method): self.method = method - def request(self, uri, method="GET", body='', headers=None, + def request(self, uri, method="GET", body=b'', headers=None, redirections=httplib2.DEFAULT_MAX_REDIRECTS, connection_type=None): DEFAULT_POST_CONTENT_TYPE = 'application/x-www-form-urlencoded' @@ -828,7 +833,7 @@ def signing_base(self, request, consumer, token): if token: key += escape(token.secret) raw = '&'.join(sig) - return key, raw + return key.encode('ascii'), raw.encode('ascii') def sign(self, request, consumer, token): """Builds the base signature string.""" @@ -854,4 +859,4 @@ def signing_base(self, request, consumer, token): def sign(self, request, consumer, token): key, raw = self.signing_base(request, consumer, token) - return raw + return raw.encode('utf8') diff --git a/oauth2/_compat.py b/oauth2/_compat.py index ed77b391..d01905b5 100644 --- a/oauth2/_compat.py +++ b/oauth2/_compat.py @@ -1,11 +1,13 @@ try: TEXT = unicode except NameError: #pragma NO COVER Py3k + PY3 = True TEXT = str STRING_TYPES = (str, bytes) def b(x, encoding='ascii'): return bytes(x, encoding) else: #pragma NO COVER Python2 + PY3 = False STRING_TYPES = (unicode, bytes) def b(x, encoding='ascii'): if isinstance(x, unicode): diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 9dcc7305..49bbe8c3 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -30,6 +30,7 @@ import httplib2 import mock +from oauth2._compat import b from oauth2._compat import unquote from oauth2._compat import urlencode from oauth2._compat import urlparse @@ -44,10 +45,12 @@ _UBLANK = u(' ') _BSMILEY = b':-)' _USMILEY = u(_BSMILEY) -_GLYPH = b'\xae' -_UGLYPH = u(_GLYPH, 'latin1') -_U2019 = u(b'\xe2\x80\x99', 'utf8') # u'\u2019' -_U2766 = u(b'\xe2\x9d\xa6', 'utf8') # u'\u2766' +_BGLYPH = b'\xae' +_UGLYPH = u(_BGLYPH, 'latin1') +_B2019 = b'\xe2\x80\x99' # u'\u2019' encoded to UTF-8 +_U2019 = u(_B2019, 'utf8') # u'\u2019' +_B2766 = b'\xe2\x9d\xa6' # u'\u2766' encoded to UTF-8 +_U2766 = u(_B2766, 'utf8') # u'\u2766' class TestError(unittest.TestCase): def test_message(self): @@ -458,7 +461,7 @@ def test_to_header(self): } req = oauth.Request("GET", realm, params) - header, value = req.to_header(realm).items()[0] + header, value = list(req.to_header(realm).items())[0] parts = value.split('OAuth ') vars = parts[1].split(', ') @@ -575,15 +578,15 @@ def test_to_url_with_query(self): self.assertEquals(exp.netloc, res.netloc) self.assertEquals(exp.path, res.path) - a = parse_qs(exp.query) - b = parse_qs(res.query) - self.assertTrue('alt' in b) - self.assertTrue('max-contacts' in b) - self.assertEquals(b['alt'], ['json']) - self.assertEquals(b['max-contacts'], ['10']) - self.assertEquals(a, b) + exp_q = parse_qs(exp.query) + res_q = parse_qs(res.query) + self.assertTrue('alt' in res_q) + self.assertTrue('max-contacts' in res_q) + self.assertEquals(res_q['alt'], ['json']) + self.assertEquals(res_q['max-contacts'], ['10']) + self.assertEquals(exp_q, res_q) - def test_signature_base_string_nonascii_nonutf8(self): + def test_signature_base_unicode_nonascii(self): consumer = oauth.Consumer('consumer_token', 'consumer_secret') url = u('http://api.simplegeo.com:80/1.0/places/address.json' @@ -595,29 +598,38 @@ def test_signature_base_string_nonascii_nonutf8(self): u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) self.failUnlessReallyEqual( - req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + req['oauth_signature'], b'WhufgeZKyYpKsI70GZaiDaYwl6g=') - url = ('http://api.simplegeo.com:80/1.0/places/address.json' - '?q=monkeys&category=animal' - '&address=41+Decatur+St,+San+Francisc\xe2\x9d\xa6,+CA') + def test_signature_base_string_bytes_nonascii_nonutf8(self): + consumer = oauth.Consumer('consumer_token', 'consumer_secret') + + url = (b'http://api.simplegeo.com:80/1.0/places/address.json' + b'?q=monkeys&category=animal' + b'&address=41+Decatur+St,+San+Francisc') + _B2766 + b',+CA' req = oauth.Request("GET", url) self.failUnlessReallyEqual( req.normalized_url, u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual( - req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + self.failUnlessReallyEqual( #XXX + req['oauth_signature'], b'WhufgeZKyYpKsI70GZaiDaYwl6g=') - url = ('http://api.simplegeo.com:80/1.0/places/address.json' - '?q=monkeys&category=animal' - '&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA') + def test_signature_base_bytes_nonascii_nonutf8_urlencoded(self): + consumer = oauth.Consumer('consumer_token', 'consumer_secret') + + url = (b'http://api.simplegeo.com:80/1.0/places/address.json' + b'?q=monkeys&category=animal' + b'&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA') req = oauth.Request("GET", url) self.failUnlessReallyEqual( req.normalized_url, u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) self.failUnlessReallyEqual( - req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + req['oauth_signature'], b'WhufgeZKyYpKsI70GZaiDaYwl6g=') + + def test_signature_base_unicode_nonascii_nonutf8_url_encoded(self): + consumer = oauth.Consumer('consumer_token', 'consumer_secret') url = u('http://api.simplegeo.com:80/1.0/places/address.json' '?q=monkeys&category=animal' @@ -628,7 +640,7 @@ def test_signature_base_string_nonascii_nonutf8(self): u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) self.failUnlessReallyEqual( - req['oauth_signature'], 'WhufgeZKyYpKsI70GZaiDaYwl6g=') + req['oauth_signature'], b'WhufgeZKyYpKsI70GZaiDaYwl6g=') def test_signature_base_string_with_query(self): url = ("https://www.google.com/m8/feeds/contacts/default/full/" @@ -646,7 +658,7 @@ def test_signature_base_string_with_query(self): self.assertEquals( req.normalized_url, 'https://www.google.com/m8/feeds/contacts/default/full/') - self.assertEquals(req.url, bytes(url)) + self.assertEquals(req.url, url) normalized_params = parse_qsl(req.get_normalized_parameters()) self.assertTrue(len(normalized_params), len(params) + 2) normalized_params = dict(normalized_params) @@ -727,14 +739,14 @@ def test_signing_base(self): consumer = oauth.Consumer('dpf43f3p2l4k3l03', 'foo') key, raw = sm.signing_base(req, consumer, None) - expected = ('GET&http%3A%2F%2Fphotos.example.net%2Fphotos' - '&file%3Dvacation.jpg' - '%26oauth_consumer_key%3Ddpf43f3p2l4k3l03' - '%26oauth_nonce%3Dkllo9940pd9333jh' - '%26oauth_signature_method%3DHMAC-SHA1' - '%26oauth_timestamp%3D1191242096' - '%26oauth_token%3Dnnch734d00sl2jdk' - '%26oauth_version%3D1.0%26size%3Doriginal') + expected = b('GET&http%3A%2F%2Fphotos.example.net%2Fphotos' + '&file%3Dvacation.jpg' + '%26oauth_consumer_key%3Ddpf43f3p2l4k3l03' + '%26oauth_nonce%3Dkllo9940pd9333jh' + '%26oauth_signature_method%3DHMAC-SHA1' + '%26oauth_timestamp%3D1191242096' + '%26oauth_token%3Dnnch734d00sl2jdk' + '%26oauth_version%3D1.0%26size%3Doriginal') self.assertEquals(expected, raw) def test_get_normalized_parameters(self): @@ -841,7 +853,7 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): # If someone passes a sequence of bytes which is not ascii for # url, we'll raise an exception as early as possible. - url = "http://sp.example.com/\x92" # It's actually cp1252-encoding... + url = b"http://sp.example.com/\x92" # It's actually cp1252-encoding... self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) @@ -850,7 +862,7 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) self.failUnlessReallyEqual( - req['oauth_signature'], 'cMzvCkhvLL57+sTIxLITTHfkqZk=') + req['oauth_signature'], b'cMzvCkhvLL57+sTIxLITTHfkqZk=') # And if it is a utf-8-encoded-then-percent-encoded non-ascii # thing, we'll decode it and use it. @@ -858,14 +870,14 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) self.failUnlessReallyEqual( - req['oauth_signature'], 'yMLKOyNKC/DkyhUOb8DLSvceEWE=') + req['oauth_signature'], b'yMLKOyNKC/DkyhUOb8DLSvceEWE=') # Same thing with the params. url = "http://sp.example.com/" # If someone passes a sequence of bytes which is not ascii in # params, we'll raise an exception as early as possible. - params['non_oauth_thing'] = '\xae', # It's actually cp1252-encoding... + params['non_oauth_thing'] = b'\xae', # It's actually cp1252-encoding... self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) @@ -874,19 +886,19 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) self.failUnlessReallyEqual( - req['oauth_signature'], '0GU50m0v60CVDB5JnoBXnvvvKx4=') + req['oauth_signature'], b'0GU50m0v60CVDB5JnoBXnvvvKx4=') # And if it is a utf-8-encoded non-ascii thing, we'll decode # it and use it. - params['non_oauth_thing'] = '\xc2\xae' + params['non_oauth_thing'] = b'\xc2\xae' req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) self.failUnlessReallyEqual( - req['oauth_signature'], 'pqOCu4qvRTiGiXB8Z61Jsey0pMM=') + req['oauth_signature'], b'pqOCu4qvRTiGiXB8Z61Jsey0pMM=') # Also if there are non-utf8 bytes in the query args. - url = "http://sp.example.com/?q=\x92" # cp1252 + url = b"http://sp.example.com/?q=\x92" # cp1252 self.assertRaises(TypeError, oauth.Request, method="GET", url=url, parameters=params) @@ -909,12 +921,12 @@ def test_request_hash_of_body(self): url = u('http://www.example.com/resource') req = oauth.Request(method="PUT", url=url, parameters=params, - body="Hello World!", is_form_encoded=False) + body=b"Hello World!", is_form_encoded=False) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) self.failUnlessReallyEqual( - req['oauth_body_hash'], 'Lve95gjOVATpfV8EL5X4nxwjKHE=') + req['oauth_body_hash'], b'Lve95gjOVATpfV8EL5X4nxwjKHE=') self.failUnlessReallyEqual( - req['oauth_signature'], 't+MX8l/0S8hdbVQL99nD0X1fPnM=') + req['oauth_signature'], b't+MX8l/0S8hdbVQL99nD0X1fPnM=') # oauth-bodyhash.html A.1 has # '08bUFF%2Fjmp59mWB7cSgCYBUpJ0U%3D', but I don't see how that # is possible. @@ -929,12 +941,12 @@ def test_request_hash_of_body(self): } req = oauth.Request(method="PUT", url=url, parameters=params, - body="Hello World!", is_form_encoded=False) + body=b"Hello World!", is_form_encoded=False) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) self.failUnlessReallyEqual( - req['oauth_body_hash'], 'Lve95gjOVATpfV8EL5X4nxwjKHE=') + req['oauth_body_hash'], b'Lve95gjOVATpfV8EL5X4nxwjKHE=') self.failUnlessReallyEqual( - req['oauth_signature'], 'CTFmrqJIGT7NsWJ42OrujahTtTc=') + req['oauth_signature'], b'CTFmrqJIGT7NsWJ42OrujahTtTc=') # Appendix A.2 params = { @@ -949,9 +961,9 @@ def test_request_hash_of_body(self): is_form_encoded=False) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) self.failUnlessReallyEqual( - req['oauth_body_hash'], '2jmj7l5rSw0yVb/vlWAYkK/YBwk=') + req['oauth_body_hash'], b'2jmj7l5rSw0yVb/vlWAYkK/YBwk=') self.failUnlessReallyEqual( - req['oauth_signature'], 'Zhl++aWSP0O3/hYQ0CuBc7jv38I=') + req['oauth_signature'], b'Zhl++aWSP0O3/hYQ0CuBc7jv38I=') def test_sign_request(self): @@ -971,8 +983,8 @@ def test_sign_request(self): req = oauth.Request(method="GET", url=url, parameters=params) methods = { - 'DX01TdHws7OninCLK9VztNTH1M4=': oauth.SignatureMethod_HMAC_SHA1(), - 'con-test-secret&tok-test-secret': + b'DX01TdHws7OninCLK9VztNTH1M4=': oauth.SignatureMethod_HMAC_SHA1(), + b'con-test-secret&tok-test-secret': oauth.SignatureMethod_PLAINTEXT() } @@ -982,30 +994,30 @@ def test_sign_request(self): self.assertEquals(req['oauth_signature'], exp) # Also if there are non-ascii chars in the URL. - url = "http://sp.example.com/\xe2\x80\x99" # utf-8 bytes + url = b"http://sp.example.com/\xe2\x80\x99" # utf-8 bytes req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) self.assertEquals( - req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') + req['oauth_signature'], b'loFvp5xC7YbOgd9exIO6TxB7H4s=') url = u('http://sp.example.com/') + _U2019 # Python unicode object req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) self.assertEquals( - req['oauth_signature'], 'loFvp5xC7YbOgd9exIO6TxB7H4s=') + req['oauth_signature'], b'loFvp5xC7YbOgd9exIO6TxB7H4s=') # Also if there are non-ascii chars in the query args. - url = "http://sp.example.com/?q=\xe2\x80\x99" # utf-8 bytes + url = b"http://sp.example.com/?q=\xe2\x80\x99" # utf-8 bytes req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) self.assertEquals( - req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') + req['oauth_signature'], b'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') url = u('http://sp.example.com/?q=') + _U2019 # Python unicode object req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) self.assertEquals( - req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') + req['oauth_signature'], b'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') def test_from_request(self): url = "http://sp.example.com/" @@ -1365,7 +1377,7 @@ def create_simple_multipart_data(self, data): ] items += ['', '--'+boundary+'--', ''] content_type = 'multipart/form-data; boundary=%s' % boundary - return content_type, crlf.join(items) + return content_type, crlf.join(items).encode('ascii') def test_init(self): class Blah(): @@ -1399,14 +1411,14 @@ def test_access_token_post(self): self.assertEquals(int(resp['status']), 200) res = dict(parse_qsl(content)) - self.assertTrue('oauth_token' in res) - self.assertTrue('oauth_token_secret' in res) + self.assertTrue(b'oauth_token' in res) + self.assertTrue(b'oauth_token_secret' in res) def _two_legged(self, method): client = oauth.Client(self.consumer, None) - return client.request(self._uri('two_legged'), method, - body=urlencode(self.body)) + body = urlencode(self.body).encode('ascii') + return client.request(self._uri('two_legged'), method, body=body) def test_two_legged_post(self): """A test of a two-legged OAuth POST request.""" @@ -1464,7 +1476,7 @@ def mockrequest(cl, ur, **kw): self.failUnlessEqual(frozenset(kw.keys()), frozenset(['method', 'body', 'redirections', 'connection_type', 'headers'])) - self.failUnlessEqual(kw['body'], '') + self.failUnlessEqual(kw['body'], b'') self.failUnlessEqual(kw['connection_type'], None) self.failUnlessEqual(kw['method'], 'GET') self.failUnlessEqual(kw['redirections'], From bb7568fc00345b7167249f6530040044f4efc429 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 5 Jun 2013 10:30:57 -0400 Subject: [PATCH 18/27] Restore correct split of urlquoted UTF-8 query strings under Python2. --- oauth2/__init__.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 342b9204..f91d9e5e 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -31,6 +31,7 @@ import httplib2 from ._compat import PY3 +from ._compat import b from ._compat import parse_qs from ._compat import quote from ._compat import STRING_TYPES @@ -614,10 +615,11 @@ def _split_header(header): @staticmethod def _split_url_string(param_str): """Turn URL string into parameters.""" - #XXX parse_qs is leaving the encoded bytes after un-escaping - #parameters = parse_qs(param_str.encode('utf-8'), - parameters = parse_qs(param_str, - keep_blank_values=True) + if not PY3: + # If passed unicode with quoted UTF8, Python2's parse_qs leaves + # mojibake'd uniocde after unquoting, so encode first. + param_str = b(param_str, 'utf-8') + parameters = parse_qs(param_str, keep_blank_values=True) for k, v in parameters.items(): parameters[k] = unquote_to_bytes(v[0]) return parameters From 4000723df389f12d88581e818f40930016b856c6 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 5 Jun 2013 10:31:19 -0400 Subject: [PATCH 19/27] Run tests under tox on Python 3.2. --- tox.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 2f886e15..6b74acfd 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ [tox] envlist = -# py26,py27,py32,py33,pypy,cover,docs - py26,py27,cover + py26,py27,py32,cover [testenv] commands = From 63db42cd547888d2ffca5248cb5fd26538a9bdf2 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 5 Jun 2013 10:43:56 -0400 Subject: [PATCH 20/27] Accomodate hash randomization. --- oauth2/__init__.py | 8 ++++---- tests/test_oauth.py | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index f91d9e5e..ac480af2 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -405,14 +405,14 @@ def to_header(self, realm=''): def to_postdata(self): """Serialize as post data for a POST request.""" - d = {} - for k, v in self.items(): - d[k.encode('utf-8')] = to_utf8_optional_iterator(v) + items = [] + for k, v in sorted(self.items()): # predictable for testing + items.append((k.encode('utf-8'), to_utf8_optional_iterator(v))) # tell urlencode to deal with sequence values and map them correctly # to resulting querystring. for example self["k"] = ["v1", "v2"] will # result in 'k=v1&k=v2' and not k=%5B%27v1%27%2C+%27v2%27%5D - return urlencode(d, True).replace('+', '%20') + return urlencode(items, True).replace('+', '%20') def to_url(self): """Serialize as a URL for a GET request.""" diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 49bbe8c3..9602fae1 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -499,13 +499,14 @@ def test_to_postdata_nonascii(self): self.failUnlessReallyEqual( req.to_postdata(), ('nonasciithing=q%C2%BFu%C3%A9%20%2Caasp%20u%3F..a.s' - '&oauth_nonce=4572616e48616d6d65724c61686176' - '&oauth_timestamp=137131200' '&oauth_consumer_key=0685bd9184jfhq22' + '&oauth_nonce=4572616e48616d6d65724c61686176' + '&oauth_signature=wOJIO9A2W5mFwDgiDvZbTSMK%252FPY%253D' '&oauth_signature_method=HMAC-SHA1' - '&oauth_version=1.0' + '&oauth_timestamp=137131200' '&oauth_token=ad180jjd733klru7' - '&oauth_signature=wOJIO9A2W5mFwDgiDvZbTSMK%252FPY%253D')) + '&oauth_version=1.0' + )) def test_to_postdata(self): realm = "http://sp.example.com/" From 1d378810c513d5edc6b9aa5a64a99efabd568e89 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 5 Jun 2013 10:46:53 -0400 Subject: [PATCH 21/27] Avoid shadowing imported 'b' function. --- tests/test_oauth.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_oauth.py b/tests/test_oauth.py index 9602fae1..def353ff 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -552,9 +552,9 @@ def test_to_url(self): self.assertEquals(exp.netloc, res.netloc) self.assertEquals(exp.path, res.path) - a = parse_qs(exp.query) - b = parse_qs(res.query) - self.assertEquals(a, b) + exp_parsed = parse_qs(exp.query) + res_parsed = parse_qs(res.query) + self.assertEquals(exp_parsed, res_parsed) def test_to_url_with_query(self): url = ("https://www.google.com/m8/feeds/contacts/default/full/" From 1f7134b11dbee65f6debe4332492638e76df6775 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 5 Jun 2013 10:52:25 -0400 Subject: [PATCH 22/27] Accomodate hash randomization moar. --- oauth2/__init__.py | 14 +++++++------- tests/test_oauth.py | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index ac480af2..11689762 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -269,15 +269,15 @@ def to_string(self): The resulting string includes the token's secret, so you should never send or store this string where a third party can read it. """ - - data = { - 'oauth_token': self.key, - 'oauth_token_secret': self.secret, - } + items = [ + ('oauth_token', self.key), + ('oauth_token_secret', self.secret), + ] if self.callback_confirmed is not None: - data['oauth_callback_confirmed'] = self.callback_confirmed - return urlencode(data) + items.append( + ('oauth_callback_confirmed', self.callback_confirmed)) + return urlencode(items) @staticmethod def from_string(s): diff --git a/tests/test_oauth.py b/tests/test_oauth.py index def353ff..fdf5bab8 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -215,8 +215,8 @@ def test_get_callback_url(self): self.assertEqual(url, '%s%s' % (cb, verifier_str)) def test_to_string(self): - string = 'oauth_token_secret=%s&oauth_token=%s' % (self.secret, - self.key) + string = 'oauth_token=%s&oauth_token_secret=%s' % ( + self.key, self.secret) self.assertEqual(self.token.to_string(), string) self.token.set_callback('http://www.example.com/my-callback') @@ -236,7 +236,7 @@ def _compare_tokens(self, new): def test___str__(self): tok = oauth.Token('tooken', 'seecret') self.assertEqual(str(tok), - 'oauth_token_secret=seecret&oauth_token=tooken') + 'oauth_token=tooken&oauth_token_secret=seecret') def test_from_string(self): self.assertRaises(ValueError, lambda: oauth.Token.from_string('')) From f26d9c0f42ba56f0b6fdcf6460ae72646a706776 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 5 Jun 2013 10:52:57 -0400 Subject: [PATCH 23/27] Run tests under tox on Python 3.3. --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 6b74acfd..8f949280 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py26,py27,py32,cover + py26,py27,py32,py33,cover [testenv] commands = From fb2b8246e650f1284c581b2fdca5c94d99af3f44 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 5 Jun 2013 10:58:32 -0400 Subject: [PATCH 24/27] Silence Py3k unittest nannyisms. --- tests/test_oauth.py | 280 ++++++++++++++++++++++---------------------- 1 file changed, 140 insertions(+), 140 deletions(-) diff --git a/tests/test_oauth.py b/tests/test_oauth.py index fdf5bab8..7d700581 100644 --- a/tests/test_oauth.py +++ b/tests/test_oauth.py @@ -69,7 +69,7 @@ def test_str(self): try: raise oauth.Error except oauth.Error as e: - self.assertEquals(str(e), 'OAuth error occurred.') + self.assertEqual(str(e), 'OAuth error occurred.') class TestGenerateFunctions(unittest.TestCase): def test_build_auth_header(self): @@ -99,16 +99,16 @@ def test_build_xoauth_string(self): var, val = part.split('=') returned[var] = val.strip('"') - self.assertEquals('HMAC-SHA1', returned['oauth_signature_method']) - self.assertEquals('user_token', returned['oauth_token']) - self.assertEquals('consumer_token', returned['oauth_consumer_key']) + self.assertEqual('HMAC-SHA1', returned['oauth_signature_method']) + self.assertEqual('user_token', returned['oauth_token']) + self.assertEqual('consumer_token', returned['oauth_consumer_key']) self.assertTrue('oauth_signature' in returned, 'oauth_signature') def test_escape(self): string = 'http://whatever.com/~someuser/?test=test&other=other' - self.assert_('~' in oauth.escape(string)) + self.assertTrue('~' in oauth.escape(string)) string = '../../../../../../../etc/passwd' - self.assert_('../' not in oauth.escape(string)) + self.assertTrue('../' not in oauth.escape(string)) def test_gen_nonce(self): nonce = oauth.generate_nonce() @@ -146,8 +146,8 @@ def test_str(self): res = dict(parse_qsl(str(self.consumer))) self.assertTrue('oauth_consumer_key' in res) self.assertTrue('oauth_consumer_secret' in res) - self.assertEquals(res['oauth_consumer_key'], self.consumer.key) - self.assertEquals(res['oauth_consumer_secret'], self.consumer.secret) + self.assertEqual(res['oauth_consumer_key'], self.consumer.key) + self.assertEqual(res['oauth_consumer_secret'], self.consumer.secret) class TestToken(unittest.TestCase): def setUp(self): @@ -274,68 +274,68 @@ def test_from_string(self): self._compare_tokens(new) class ReallyEqualMixin: - def failUnlessReallyEqual(self, a, b, msg=None): - self.failUnlessEqual(a, b, msg=msg) - self.failUnlessEqual(type(a), type(b), + def assertReallyEqual(self, a, b, msg=None): + self.assertEqual(a, b, msg=msg) + self.assertEqual(type(a), type(b), msg="a :: %r, b :: %r, %r" % (a, b, msg)) class TestFuncs(unittest.TestCase): def test_to_unicode(self): - self.failUnlessRaises(TypeError, oauth.to_unicode, 0) - self.failUnlessRaises(TypeError, oauth.to_unicode, b'\xae') - self.failUnlessRaises(TypeError, + self.assertRaises(TypeError, oauth.to_unicode, 0) + self.assertRaises(TypeError, oauth.to_unicode, b'\xae') + self.assertRaises(TypeError, oauth.to_unicode_optional_iterator, b'\xae') - self.failUnlessRaises(TypeError, + self.assertRaises(TypeError, oauth.to_unicode_optional_iterator, [b'\xae']) - self.failUnlessEqual(oauth.to_unicode(_BSMILEY), _USMILEY) - self.failUnlessEqual(oauth.to_unicode(_UGLYPH), _UGLYPH) - self.failUnlessEqual(oauth.to_unicode(b'\xc2\xae'), _UGLYPH) + self.assertEqual(oauth.to_unicode(_BSMILEY), _USMILEY) + self.assertEqual(oauth.to_unicode(_UGLYPH), _UGLYPH) + self.assertEqual(oauth.to_unicode(b'\xc2\xae'), _UGLYPH) def test_to_utf8(self): - self.failUnlessRaises(TypeError, oauth.to_utf8, 0) - self.failUnlessRaises(TypeError, oauth.to_utf8, b'\x81') - self.failUnlessEqual(oauth.to_utf8(_BSMILEY), _BSMILEY) - self.failUnlessEqual(oauth.to_utf8(_UGLYPH), + self.assertRaises(TypeError, oauth.to_utf8, 0) + self.assertRaises(TypeError, oauth.to_utf8, b'\x81') + self.assertEqual(oauth.to_utf8(_BSMILEY), _BSMILEY) + self.assertEqual(oauth.to_utf8(_UGLYPH), _UGLYPH.encode('utf8')) def test_to_unicode_if_string(self): - self.failUnless(oauth.to_unicode_if_string(self) is self) - self.failUnlessEqual(oauth.to_unicode_if_string(_BSMILEY), _USMILEY) + self.assertTrue(oauth.to_unicode_if_string(self) is self) + self.assertEqual(oauth.to_unicode_if_string(_BSMILEY), _USMILEY) def test_to_utf8_if_string(self): - self.failUnless(oauth.to_utf8_if_string(self) is self) - self.failUnlessEqual(oauth.to_utf8_if_string(_USMILEY), _BSMILEY) - self.failUnlessEqual(oauth.to_utf8_if_string(_UGLYPH), + self.assertTrue(oauth.to_utf8_if_string(self) is self) + self.assertEqual(oauth.to_utf8_if_string(_USMILEY), _BSMILEY) + self.assertEqual(oauth.to_utf8_if_string(_UGLYPH), _UGLYPH.encode('utf8')) def test_to_unicode_optional_iterator(self): - self.failUnlessEqual(oauth.to_unicode_optional_iterator(_BSMILEY), + self.assertEqual(oauth.to_unicode_optional_iterator(_BSMILEY), _USMILEY) - self.failUnlessEqual(oauth.to_unicode_optional_iterator(_UGLYPH), + self.assertEqual(oauth.to_unicode_optional_iterator(_UGLYPH), _UGLYPH) - self.failUnlessEqual(oauth.to_unicode_optional_iterator([_BSMILEY]), + self.assertEqual(oauth.to_unicode_optional_iterator([_BSMILEY]), [_USMILEY]) - self.failUnlessEqual(oauth.to_unicode_optional_iterator([_UGLYPH]), + self.assertEqual(oauth.to_unicode_optional_iterator([_UGLYPH]), [_UGLYPH]) - self.failUnlessEqual(oauth.to_unicode_optional_iterator((_UGLYPH,)), + self.assertEqual(oauth.to_unicode_optional_iterator((_UGLYPH,)), [_UGLYPH]) - self.failUnless(oauth.to_unicode_optional_iterator(self) is self) + self.assertTrue(oauth.to_unicode_optional_iterator(self) is self) def test_to_utf8_optional_iterator(self): - self.failUnlessEqual(oauth.to_utf8_optional_iterator(_BSMILEY), + self.assertEqual(oauth.to_utf8_optional_iterator(_BSMILEY), _BSMILEY) - self.failUnlessEqual(oauth.to_utf8_optional_iterator(_UGLYPH), + self.assertEqual(oauth.to_utf8_optional_iterator(_UGLYPH), _UGLYPH.encode('utf8')) - self.failUnlessEqual(oauth.to_utf8_optional_iterator([_BSMILEY]), + self.assertEqual(oauth.to_utf8_optional_iterator([_BSMILEY]), [_BSMILEY]) - self.failUnlessEqual(oauth.to_utf8_optional_iterator([_USMILEY]), + self.assertEqual(oauth.to_utf8_optional_iterator([_USMILEY]), [_BSMILEY]) - self.failUnlessEqual(oauth.to_utf8_optional_iterator([_UGLYPH]), + self.assertEqual(oauth.to_utf8_optional_iterator([_UGLYPH]), [_UGLYPH.encode('utf8')]) - self.failUnlessEqual(oauth.to_utf8_optional_iterator((_UGLYPH,)), + self.assertEqual(oauth.to_utf8_optional_iterator((_UGLYPH,)), [_UGLYPH.encode('utf8')]) - self.failUnless(oauth.to_utf8_optional_iterator(self) is self) + self.assertTrue(oauth.to_utf8_optional_iterator(self) is self) class TestRequest(unittest.TestCase, ReallyEqualMixin): def test__init__(self): @@ -374,12 +374,12 @@ def test_url(self): method = "GET" req = oauth.Request(method, url1) - self.assertEquals(req.normalized_url, exp1) - self.assertEquals(req.url, url1) + self.assertEqual(req.normalized_url, exp1) + self.assertEqual(req.url, url1) req = oauth.Request(method, url2) - self.assertEquals(req.normalized_url, exp2) - self.assertEquals(req.url, url2) + self.assertEqual(req.normalized_url, exp2) + self.assertEqual(req.url, url2) def test_bad_url(self): request = oauth.Request() @@ -396,8 +396,8 @@ def test_unset_consumer_and_token(self): request.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, token) - self.assertEquals(consumer.key, request['oauth_consumer_key']) - self.assertEquals(token.key, request['oauth_token']) + self.assertEqual(consumer.key, request['oauth_consumer_key']) + self.assertEqual(token.key, request['oauth_token']) def test_no_url_set(self): consumer = oauth.Consumer('my_consumer_key', 'my_consumer_secret') @@ -413,8 +413,8 @@ def test_url_query(self): normalized_url = urlunparse(urlparse(url)[:3] + (None, None, None)) method = "GET" req = oauth.Request(method, url) - self.assertEquals(req.url, url) - self.assertEquals(req.normalized_url, normalized_url) + self.assertEqual(req.url, url) + self.assertEqual(req.normalized_url, normalized_url) def test_get_parameter(self): url = "http://example.com" @@ -422,7 +422,7 @@ def test_get_parameter(self): params = {'oauth_consumer' : 'asdf'} req = oauth.Request(method, url, parameters=params) - self.assertEquals(req.get_parameter('oauth_consumer'), 'asdf') + self.assertEqual(req.get_parameter('oauth_consumer'), 'asdf') self.assertRaises(oauth.Error, req.get_parameter, 'blah') def test_get_nonoauth_parameters(self): @@ -445,7 +445,7 @@ def test_get_nonoauth_parameters(self): params.update(other_params) req = oauth.Request("GET", "http://example.com", params) - self.assertEquals(other_params, req.get_nonoauth_parameters()) + self.assertEqual(other_params, req.get_nonoauth_parameters()) def test_to_header(self): realm = "http://sp.example.com/" @@ -472,13 +472,13 @@ def test_to_header(self): var, val = v.split('=') res[var] = unquote(val.strip('"')) - self.assertEquals(realm, res['realm']) + self.assertEqual(realm, res['realm']) del res['realm'] self.assertTrue(len(res), len(params)) for key, val in res.items(): - self.assertEquals(val, params.get(key)) + self.assertEqual(val, params.get(key)) def test_to_postdata_nonascii(self): realm = "http://sp.example.com/" @@ -496,7 +496,7 @@ def test_to_postdata_nonascii(self): req = oauth.Request("GET", realm, params) - self.failUnlessReallyEqual( + self.assertReallyEqual( req.to_postdata(), ('nonasciithing=q%C2%BFu%C3%A9%20%2Caasp%20u%3F..a.s' '&oauth_consumer_key=0685bd9184jfhq22' @@ -528,7 +528,7 @@ def test_to_postdata(self): del params['multi'] flat.extend(params.items()) kf = lambda x: x[0] - self.assertEquals( + self.assertEqual( sorted(flat, key=kf), sorted(parse_qsl(req.to_postdata()), key=kf)) @@ -548,13 +548,13 @@ def test_to_url(self): req = oauth.Request("GET", url, params) exp = urlparse("%s?%s" % (url, urlencode(params))) res = urlparse(req.to_url()) - self.assertEquals(exp.scheme, res.scheme) - self.assertEquals(exp.netloc, res.netloc) - self.assertEquals(exp.path, res.path) + self.assertEqual(exp.scheme, res.scheme) + self.assertEqual(exp.netloc, res.netloc) + self.assertEqual(exp.path, res.path) exp_parsed = parse_qs(exp.query) res_parsed = parse_qs(res.query) - self.assertEquals(exp_parsed, res_parsed) + self.assertEqual(exp_parsed, res_parsed) def test_to_url_with_query(self): url = ("https://www.google.com/m8/feeds/contacts/default/full/" @@ -575,17 +575,17 @@ def test_to_url_with_query(self): # ones with & exp = urlparse("%s&%s" % (url, urlencode(params))) res = urlparse(req.to_url()) - self.assertEquals(exp.scheme, res.scheme) - self.assertEquals(exp.netloc, res.netloc) - self.assertEquals(exp.path, res.path) + self.assertEqual(exp.scheme, res.scheme) + self.assertEqual(exp.netloc, res.netloc) + self.assertEqual(exp.path, res.path) exp_q = parse_qs(exp.query) res_q = parse_qs(res.query) self.assertTrue('alt' in res_q) self.assertTrue('max-contacts' in res_q) - self.assertEquals(res_q['alt'], ['json']) - self.assertEquals(res_q['max-contacts'], ['10']) - self.assertEquals(exp_q, res_q) + self.assertEqual(res_q['alt'], ['json']) + self.assertEqual(res_q['max-contacts'], ['10']) + self.assertEqual(exp_q, res_q) def test_signature_base_unicode_nonascii(self): consumer = oauth.Consumer('consumer_token', 'consumer_secret') @@ -594,11 +594,11 @@ def test_signature_base_unicode_nonascii(self): '?q=monkeys&category=animal' '&address=41+Decatur+St,+San+Francisc') + _U2766 + u(',+CA') req = oauth.Request("GET", url) - self.failUnlessReallyEqual( + self.assertReallyEqual( req.normalized_url, u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_signature'], b'WhufgeZKyYpKsI70GZaiDaYwl6g=') def test_signature_base_string_bytes_nonascii_nonutf8(self): @@ -608,11 +608,11 @@ def test_signature_base_string_bytes_nonascii_nonutf8(self): b'?q=monkeys&category=animal' b'&address=41+Decatur+St,+San+Francisc') + _B2766 + b',+CA' req = oauth.Request("GET", url) - self.failUnlessReallyEqual( + self.assertReallyEqual( req.normalized_url, u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual( #XXX + self.assertReallyEqual( #XXX req['oauth_signature'], b'WhufgeZKyYpKsI70GZaiDaYwl6g=') def test_signature_base_bytes_nonascii_nonutf8_urlencoded(self): @@ -622,11 +622,11 @@ def test_signature_base_bytes_nonascii_nonutf8_urlencoded(self): b'?q=monkeys&category=animal' b'&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA') req = oauth.Request("GET", url) - self.failUnlessReallyEqual( + self.assertReallyEqual( req.normalized_url, u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_signature'], b'WhufgeZKyYpKsI70GZaiDaYwl6g=') def test_signature_base_unicode_nonascii_nonutf8_url_encoded(self): @@ -636,11 +636,11 @@ def test_signature_base_unicode_nonascii_nonutf8_url_encoded(self): '?q=monkeys&category=animal' '&address=41+Decatur+St,+San+Francisc%E2%9D%A6,+CA') req = oauth.Request("GET", url) - self.failUnlessReallyEqual( + self.assertReallyEqual( req.normalized_url, u('http://api.simplegeo.com/1.0/places/address.json')) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None) - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_signature'], b'WhufgeZKyYpKsI70GZaiDaYwl6g=') def test_signature_base_string_with_query(self): @@ -656,19 +656,19 @@ def test_signature_base_string_with_query(self): 'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D", } req = oauth.Request("GET", url, params) - self.assertEquals( + self.assertEqual( req.normalized_url, 'https://www.google.com/m8/feeds/contacts/default/full/') - self.assertEquals(req.url, url) + self.assertEqual(req.url, url) normalized_params = parse_qsl(req.get_normalized_parameters()) self.assertTrue(len(normalized_params), len(params) + 2) normalized_params = dict(normalized_params) for key, value in params.items(): if key == 'oauth_signature': continue - self.assertEquals(value, normalized_params[key]) - self.assertEquals(normalized_params['alt'], 'json') - self.assertEquals(normalized_params['max-contacts'], '10') + self.assertEqual(value, normalized_params[key]) + self.assertEqual(normalized_params['alt'], 'json') + self.assertEqual(normalized_params['max-contacts'], '10') def test_get_normalized_parameters_empty(self): url = "http://sp.example.com/?empty=" @@ -679,7 +679,7 @@ def test_get_normalized_parameters_empty(self): expected='empty=' - self.assertEquals(expected, res) + self.assertEqual(expected, res) def test_get_normalized_parameters_duplicate(self): url = ("http://example.com/v2/search/videos" @@ -697,7 +697,7 @@ def test_get_normalized_parameters_duplicate(self): '&oauth_timestamp=1295397962&oauth_version=1.0' '&offset=10&q=car') - self.assertEquals(expected, res) + self.assertEqual(expected, res) def test_get_normalized_parameters_from_url(self): # example copied from @@ -720,7 +720,7 @@ def test_get_normalized_parameters_from_url(self): '&oauth_timestamp=1191242096&oauth_token=nnch734d00sl2jdk' '&oauth_version=1.0&size=original') - self.assertEquals(expected, res) + self.assertEqual(expected, res) def test_signing_base(self): # example copied from @@ -748,7 +748,7 @@ def test_signing_base(self): '%26oauth_timestamp%3D1191242096' '%26oauth_token%3Dnnch734d00sl2jdk' '%26oauth_version%3D1.0%26size%3Doriginal') - self.assertEquals(expected, raw) + self.assertEqual(expected, raw) def test_get_normalized_parameters(self): url = "http://sp.example.com/" @@ -780,7 +780,7 @@ def test_get_normalized_parameters(self): '&oauth_version=1.0' '&uni_unicode_object=%C2%AE&uni_utf8_bytes=%C2%AE') - self.assertEquals(expected, res) + self.assertEqual(expected, res) def test_get_normalized_parameters_ignores_auth_signature(self): url = "http://sp.example.com/" @@ -800,7 +800,7 @@ def test_get_normalized_parameters_ignores_auth_signature(self): res = req.get_normalized_parameters() - self.assertNotEquals(urlencode(sorted(params.items())), res) + self.assertNotEqual(urlencode(sorted(params.items())), res) foo = params.copy() del foo["oauth_signature"] @@ -821,7 +821,7 @@ class Blah: m = oauth.SignatureMethod_HMAC_SHA1() client.set_signature_method(m) - self.assertEquals(m, client.method) + self.assertEqual(m, client.method) def test_get_normalized_string_escapes_spaces_properly(self): url = "http://sp.example.com/" @@ -862,7 +862,7 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): url = u('http://sp.example.com/') + _U2019 req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_signature'], b'cMzvCkhvLL57+sTIxLITTHfkqZk=') # And if it is a utf-8-encoded-then-percent-encoded non-ascii @@ -870,7 +870,7 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): url = "http://sp.example.com/%E2%80%99" req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_signature'], b'yMLKOyNKC/DkyhUOb8DLSvceEWE=') # Same thing with the params. @@ -886,7 +886,7 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): params['non_oauth_thing'] = _U2019 req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_signature'], b'0GU50m0v60CVDB5JnoBXnvvvKx4=') # And if it is a utf-8-encoded non-ascii thing, we'll decode @@ -894,7 +894,7 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp): params['non_oauth_thing'] = b'\xc2\xae' req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_signature'], b'pqOCu4qvRTiGiXB8Z61Jsey0pMM=') @@ -924,9 +924,9 @@ def test_request_hash_of_body(self): req = oauth.Request(method="PUT", url=url, parameters=params, body=b"Hello World!", is_form_encoded=False) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_body_hash'], b'Lve95gjOVATpfV8EL5X4nxwjKHE=') - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_signature'], b't+MX8l/0S8hdbVQL99nD0X1fPnM=') # oauth-bodyhash.html A.1 has # '08bUFF%2Fjmp59mWB7cSgCYBUpJ0U%3D', but I don't see how that @@ -944,9 +944,9 @@ def test_request_hash_of_body(self): req = oauth.Request(method="PUT", url=url, parameters=params, body=b"Hello World!", is_form_encoded=False) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_body_hash'], b'Lve95gjOVATpfV8EL5X4nxwjKHE=') - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_signature'], b'CTFmrqJIGT7NsWJ42OrujahTtTc=') # Appendix A.2 @@ -961,9 +961,9 @@ def test_request_hash_of_body(self): req = oauth.Request(method="GET", url=url, parameters=params, is_form_encoded=False) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, None) - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_body_hash'], b'2jmj7l5rSw0yVb/vlWAYkK/YBwk=') - self.failUnlessReallyEqual( + self.assertReallyEqual( req['oauth_signature'], b'Zhl++aWSP0O3/hYQ0CuBc7jv38I=') @@ -991,33 +991,33 @@ def test_sign_request(self): for exp, method in methods.items(): req.sign_request(method, con, tok) - self.assertEquals(req['oauth_signature_method'], method.name) - self.assertEquals(req['oauth_signature'], exp) + self.assertEqual(req['oauth_signature_method'], method.name) + self.assertEqual(req['oauth_signature'], exp) # Also if there are non-ascii chars in the URL. url = b"http://sp.example.com/\xe2\x80\x99" # utf-8 bytes req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals( + self.assertEqual( req['oauth_signature'], b'loFvp5xC7YbOgd9exIO6TxB7H4s=') url = u('http://sp.example.com/') + _U2019 # Python unicode object req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals( + self.assertEqual( req['oauth_signature'], b'loFvp5xC7YbOgd9exIO6TxB7H4s=') # Also if there are non-ascii chars in the query args. url = b"http://sp.example.com/?q=\xe2\x80\x99" # utf-8 bytes req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals( + self.assertEqual( req['oauth_signature'], b'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') url = u('http://sp.example.com/?q=') + _U2019 # Python unicode object req = oauth.Request(method="GET", url=url, parameters=params) req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok) - self.assertEquals( + self.assertEqual( req['oauth_signature'], b'IBw5mfvoCsDjgpcsVKbyvsDqQaU=') def test_from_request(self): @@ -1038,10 +1038,10 @@ def test_from_request(self): # Test from the headers req = oauth.Request.from_request("GET", url, headers) - self.assertEquals(req.method, "GET") - self.assertEquals(req.url, url) + self.assertEqual(req.method, "GET") + self.assertEqual(req.url, url) - self.assertEquals(params, req.copy()) + self.assertEqual(params, req.copy()) # Test with bad OAuth headers bad_headers = { @@ -1059,11 +1059,11 @@ def test_from_request(self): for k, v in exp.items(): exp[k] = unquote(v[0]) - self.assertEquals(exp, req.copy()) + self.assertEqual(exp, req.copy()) # Test that a boned from_request() call returns None req = oauth.Request.from_request("GET", url) - self.assertEquals(None, req) + self.assertEqual(None, req) def test_from_token_and_callback(self): url = "http://sp.example.com/" @@ -1081,11 +1081,11 @@ def test_from_token_and_callback(self): tok = oauth.Token(key="tok-test-key", secret="tok-test-secret") req = oauth.Request.from_token_and_callback(tok) self.assertFalse('oauth_callback' in req) - self.assertEquals(req['oauth_token'], tok.key) + self.assertEqual(req['oauth_token'], tok.key) req = oauth.Request.from_token_and_callback(tok, callback=url) self.assertTrue('oauth_callback' in req) - self.assertEquals(req['oauth_callback'], url) + self.assertEqual(req['oauth_callback'], url) def test_from_consumer_and_token(self): url = "http://sp.example.com/" @@ -1096,9 +1096,9 @@ def test_from_consumer_and_token(self): req = oauth.Request.from_consumer_and_token(con, token=tok, http_method="GET", http_url=url) - self.assertEquals(req['oauth_token'], tok.key) - self.assertEquals(req['oauth_consumer_key'], con.key) - self.assertEquals(tok.verifier, req['oauth_verifier']) + self.assertEqual(req['oauth_token'], tok.key) + self.assertEqual(req['oauth_consumer_key'], con.key) + self.assertEqual(tok.verifier, req['oauth_verifier']) class SignatureMethod_Bad(oauth.SignatureMethod): name = "BAD" @@ -1142,7 +1142,7 @@ def test_init(self): oauth.SignatureMethod_HMAC_SHA1)) server = oauth.Server() - self.assertEquals(server.signature_methods, {}) + self.assertEqual(server.signature_methods, {}) def test_add_signature_method(self): server = oauth.Server() @@ -1168,9 +1168,9 @@ def test_verify_request(self): self.assertTrue('bar' in parameters) self.assertTrue('foo' in parameters) self.assertTrue('multi' in parameters) - self.assertEquals(parameters['bar'], 'blerg') - self.assertEquals(parameters['foo'], 59) - self.assertEquals(parameters['multi'], ['FOO','BAR']) + self.assertEqual(parameters['bar'], 'blerg') + self.assertEqual(parameters['foo'], 59) + self.assertEqual(parameters['multi'], ['FOO','BAR']) def test_verify_request_missing_signature(self): from oauth2 import MissingSignature @@ -1202,7 +1202,7 @@ def test_build_authenticate_header(self): server = oauth.Server() headers = server.build_authenticate_header('example.com') self.assertTrue('WWW-Authenticate' in headers) - self.assertEquals('OAuth realm="example.com"', + self.assertEqual('OAuth realm="example.com"', headers['WWW-Authenticate']) def test_no_version(self): @@ -1402,14 +1402,14 @@ def test_access_token_get(self): client = oauth.Client(self.consumer, None) resp, content = client.request(self._uri('request_token'), "GET") - self.assertEquals(int(resp['status']), 200) + self.assertEqual(int(resp['status']), 200) def test_access_token_post(self): """Test getting an access token via POST.""" client = oauth.Client(self.consumer, None) resp, content = client.request(self._uri('request_token'), "POST") - self.assertEquals(int(resp['status']), 200) + self.assertEqual(int(resp['status']), 200) res = dict(parse_qsl(content)) self.assertTrue(b'oauth_token' in res) @@ -1425,12 +1425,12 @@ def test_two_legged_post(self): """A test of a two-legged OAuth POST request.""" resp, content = self._two_legged("POST") - self.assertEquals(int(resp['status']), 200) + self.assertEqual(int(resp['status']), 200) def test_two_legged_get(self): """A test of a two-legged OAuth GET request.""" resp, content = self._two_legged("GET") - self.assertEquals(int(resp['status']), 200) + self.assertEqual(int(resp['status']), 200) @mock.patch('httplib2.Http.request') def test_multipart_post_does_not_alter_body(self, mockHttpRequest): @@ -1445,17 +1445,17 @@ def test_multipart_post_does_not_alter_body(self, mockHttpRequest): uri = self._uri('two_legged') def mockrequest(cl, ur, **kw): - self.failUnless(cl is client) - self.failUnless(ur is uri) - self.failUnlessEqual(frozenset(kw.keys()), + self.assertTrue(cl is client) + self.assertTrue(ur is uri) + self.assertEqual(frozenset(kw.keys()), frozenset(['method', 'body', 'redirections', 'connection_type', 'headers'])) - self.failUnlessEqual(kw['body'], body) - self.failUnlessEqual(kw['connection_type'], None) - self.failUnlessEqual(kw['method'], 'POST') - self.failUnlessEqual(kw['redirections'], + self.assertEqual(kw['body'], body) + self.assertEqual(kw['connection_type'], None) + self.assertEqual(kw['method'], 'POST') + self.assertEqual(kw['redirections'], httplib2.DEFAULT_MAX_REDIRECTS) - self.failUnless(isinstance(kw['headers'], dict)) + self.assertTrue(isinstance(kw['headers'], dict)) return random_result @@ -1473,16 +1473,16 @@ def test_url_with_query_string(self, mockHttpRequest): random_result = random.randint(1,100) def mockrequest(cl, ur, **kw): - self.failUnless(cl is client) - self.failUnlessEqual(frozenset(kw.keys()), + self.assertTrue(cl is client) + self.assertEqual(frozenset(kw.keys()), frozenset(['method', 'body', 'redirections', 'connection_type', 'headers'])) - self.failUnlessEqual(kw['body'], b'') - self.failUnlessEqual(kw['connection_type'], None) - self.failUnlessEqual(kw['method'], 'GET') - self.failUnlessEqual(kw['redirections'], + self.assertEqual(kw['body'], b'') + self.assertEqual(kw['connection_type'], None) + self.assertEqual(kw['method'], 'GET') + self.assertEqual(kw['redirections'], httplib2.DEFAULT_MAX_REDIRECTS) - self.failUnless(isinstance(kw['headers'], dict)) + self.assertTrue(isinstance(kw['headers'], dict)) req = oauth.Request.from_consumer_and_token(self.consumer, None, http_method='GET', http_url=uri, parameters={}) @@ -1491,12 +1491,12 @@ def mockrequest(cl, ur, **kw): expected = parse_qsl( urlparse(req.to_url()).query) actual = parse_qsl(urlparse(ur).query) - self.failUnlessEqual(len(expected), len(actual)) + self.assertEqual(len(expected), len(actual)) actual = dict(actual) for key, value in expected: if key not in ('oauth_signature', 'oauth_nonce', 'oauth_timestamp'): - self.failUnlessEqual(actual[key], value) + self.assertEqual(actual[key], value) return random_result @@ -1516,12 +1516,12 @@ def test_multiple_values_for_a_key(self, client.request('http://whatever', 'POST', body='multi=1&multi=2') - self.failUnlessEqual(mockReqConstructor.call_count, 1) - self.failUnlessEqual(mockReqConstructor.call_args[1]['parameters'], + self.assertEqual(mockReqConstructor.call_count, 1) + self.assertEqual(mockReqConstructor.call_args[1]['parameters'], {'multi': ['1', '2']}) - self.failUnless('multi=1' in mockHttpRequest.call_args[1]['body']) - self.failUnless('multi=2' in mockHttpRequest.call_args[1]['body']) + self.assertTrue('multi=1' in mockHttpRequest.call_args[1]['body']) + self.assertTrue('multi=2' in mockHttpRequest.call_args[1]['body']) if __name__ == "__main__": import os From b042c227ec0233747a59db310b937601538b4064 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 5 Jun 2013 11:05:41 -0400 Subject: [PATCH 25/27] Note support for Python 3.2 and 3.3. --- setup.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.py b/setup.py index 66176b6f..b71e9acf 100755 --- a/setup.py +++ b/setup.py @@ -37,6 +37,9 @@ "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.2", + "Programming Language :: Python :: 3.3", "Programming Language :: Python :: Implementation :: CPython", ], packages = find_packages(), From 05e2ffb6ccb778de11d59ca0b08ad63ecc1169bb Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 5 Jun 2013 14:44:28 -0400 Subject: [PATCH 26/27] Remove spurious 'str()' conversion. --- oauth2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index 11689762..dec130a2 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -393,7 +393,7 @@ def to_header(self, realm=''): """Serialize as a header for an HTTPAuth request.""" oauth_params = ((k, v) for k, v in self.items() if k.startswith('oauth_')) - stringy_params = ((k, escape(str(v))) for k, v in oauth_params) + stringy_params = ((k, escape(v)) for k, v in oauth_params) header_params = ('%s="%s"' % (k, v) for k, v in stringy_params) params_header = ', '.join(header_params) From 0406587033cf35bd7e59b41ea98eaa4e79fd6239 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Wed, 5 Jun 2013 14:45:27 -0400 Subject: [PATCH 27/27] Ensure we parse qs as test in Token.from_string. --- oauth2/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/oauth2/__init__.py b/oauth2/__init__.py index dec130a2..1239b339 100644 --- a/oauth2/__init__.py +++ b/oauth2/__init__.py @@ -36,6 +36,7 @@ from ._compat import quote from ._compat import STRING_TYPES from ._compat import TEXT +from ._compat import u from ._compat import unquote from ._compat import unquote_to_bytes from ._compat import urlencode @@ -287,7 +288,7 @@ def from_string(s): if not len(s): raise ValueError("Invalid parameter string.") - params = parse_qs(s, keep_blank_values=False) + params = parse_qs(u(s), keep_blank_values=False) if not len(params): raise ValueError("Invalid parameter string.")