From 0e9a595b144072b2c31098a1b15aef0937811a13 Mon Sep 17 00:00:00 2001
From: Greg Roodt <groodt@gmail.com>
Date: Tue, 16 Nov 2010 20:59:45 +0000
Subject: [PATCH 01/73] Fix to support matrix params in path

---
 oauth2/__init__.py  | 4 ++--
 tests/test_oauth.py | 6 ++++++
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 65c3f071..307b960e 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -278,7 +278,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, query, fragment = urlparse.urlsplit(value)
 
             # Exclude default port numbers.
             if scheme == 'http' and netloc[-3:] == ':80':
@@ -289,7 +289,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((scheme, netloc, path, None, None, None))
+            self.normalized_url = urlparse.urlunsplit((scheme, netloc, path, None, None))
         else:
             self.normalized_url = None
             self.__dict__['url'] = None
diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index e2a87f97..93a4e241 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -528,6 +528,12 @@ def test_get_normalized_parameters_ignores_auth_signature(self):
         foo = params.copy()
         del foo["oauth_signature"]
         self.assertEqual(urllib.urlencode(sorted(foo.items())), res)
+        
+    def test_signature_base_string_with_matrix_params(self):
+        url = "http://social.yahooapis.com/v1/user/6677/connections;start=0;count=20"
+        req = oauth.Request("GET", url, None)
+        self.assertEquals(req.normalized_url, 'http://social.yahooapis.com/v1/user/6677/connections;start=0;count=20')
+        self.assertEquals(req.url, 'http://social.yahooapis.com/v1/user/6677/connections;start=0;count=20')
 
     def test_set_signature_method(self):
         consumer = oauth.Consumer('key', 'secret')

From 32e85109c656e8607d486c0f2f1b0189960c70ee Mon Sep 17 00:00:00 2001
From: Jon Parise <jon@indelible.org>
Date: Tue, 16 Aug 2011 17:28:05 -0700
Subject: [PATCH 02/73] Adding support for repeated URL parameters.

It is legal for a URL parameter to be repeated multiple times in the
query string with different values.  The previous code was only using
the first occurence of the parameter when normalizing the URL, which
ultimately causes a signature mismatch.
---
 oauth2/__init__.py  |  9 ++++++---
 tests/test_oauth.py | 12 ++++++++++++
 2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 835270e3..095614c3 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -470,11 +470,11 @@ def get_normalized_parameters(self):
         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_optional_iterator(v)) for k, v in url_items if k != 'oauth_signature' ]
         items.extend(url_items)
 
         items.sort()
-        encoded_str = urllib.urlencode(items)
+        encoded_str = urllib.urlencode(items, True)
         # 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)
@@ -608,7 +608,10 @@ 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])
+            if len(v) == 1:
+                parameters[k] = urllib.unquote(v[0])
+            else:
+                parameters[k] = sorted([urllib.unquote(s) for s in v])
         return parameters
 
 
diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index 099e5794..f212fc65 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -574,6 +574,18 @@ def test_get_normalized_parameters_duplicate(self):
 
         self.assertEquals(expected, res)
 
+    def test_get_normalized_parameters_multiple(self):
+        url = "http://example.com/v2/search/videos?oauth_nonce=79815175&oauth_timestamp=1295397962&oauth_consumer_key=mykey&oauth_signature_method=HMAC-SHA1&oauth_version=1.0&offset=10&oauth_signature=spWLI%2FGQjid7sQVd5%2FarahRxzJg%3D&tag=one&tag=two"
+
+        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&tag=one&tag=two'
+
+        self.assertEquals(expected, res)
+
+
     def test_get_normalized_parameters_from_url(self):
         # example copied from
         # https://github.com/ciaranj/node-oauth/blob/master/tests/oauth.js

From c6dc215acd10e9f0feab5109fc75b1c3f8bc4633 Mon Sep 17 00:00:00 2001
From: Simple Geebus <nerds+geebus@simplegeo.com>
Date: Fri, 9 Dec 2011 01:17:31 +0000
Subject: [PATCH 03/73] Prepared changelog for build 210

---
 debian/changelog   | 6 ++++++
 oauth2/_version.py | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/debian/changelog b/debian/changelog
index 4c6a2880..420b9e07 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-oauth2 (1.5.210) maverick; urgency=low
+
+  * UNRELEASED
+
+ -- SimpleGeo Nerds <nerds@simplegeo.com>  Fri, 09 Dec 2011 01:17:31 +0000
+
 python-oauth2 (1.5.170) lucid; urgency=low
 
   * UNRELEASED
diff --git a/oauth2/_version.py b/oauth2/_version.py
index 0d74e056..52facf5e 100644
--- a/oauth2/_version.py
+++ b/oauth2/_version.py
@@ -4,7 +4,7 @@
 
 
 
-auto_build_num = "170"
+auto_build_num = "210"
 
 
 

From a83f4a297336b631e75cba102910c19231518159 Mon Sep 17 00:00:00 2001
From: Jenkins slave user <jenkinsslave@simplegeo.com>
Date: Tue, 13 Dec 2011 17:39:07 +0000
Subject: [PATCH 04/73] Prepared changelog for build 211

---
 debian/changelog   | 6 ++++++
 oauth2/_version.py | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/debian/changelog b/debian/changelog
index 420b9e07..7a462a73 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+python-oauth2 (1.5.211) maverick; urgency=low
+
+  * UNRELEASED
+
+ -- SimpleGeo Nerds <nerds@simplegeo.com>  Tue, 13 Dec 2011 17:39:07 +0000
+
 python-oauth2 (1.5.210) maverick; urgency=low
 
   * UNRELEASED
diff --git a/oauth2/_version.py b/oauth2/_version.py
index 52facf5e..9d779eaa 100644
--- a/oauth2/_version.py
+++ b/oauth2/_version.py
@@ -4,7 +4,7 @@
 
 
 
-auto_build_num = "210"
+auto_build_num = "211"
 
 
 

From 6f58f7f690ec0f989fa5962a10e29984e4c1f5a4 Mon Sep 17 00:00:00 2001
From: Tres Seaver <tseaver@palladion.com>
Date: Tue, 28 May 2013 15:19:35 -0400
Subject: [PATCH 05/73] 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 <tseaver@palladion.com>
Date: Tue, 28 May 2013 15:21:13 -0400
Subject: [PATCH 06/73] 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 <tseaver@palladion.com>
Date: Tue, 28 May 2013 15:25:39 -0400
Subject: [PATCH 07/73] 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 <tseaver@palladion.com>
Date: Tue, 28 May 2013 16:36:37 -0400
Subject: [PATCH 08/73] 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 <tseaver@palladion.com>
Date: Tue, 28 May 2013 16:51:14 -0400
Subject: [PATCH 09/73] 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 <tseaver@palladion.com>
Date: Tue, 28 May 2013 16:53:15 -0400
Subject: [PATCH 10/73] 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 <tseaver@palladion.com>
Date: Wed, 29 May 2013 15:55:22 -0400
Subject: [PATCH 11/73] 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 <tseaver@palladion.com>
Date: Wed, 29 May 2013 16:11:41 -0400
Subject: [PATCH 12/73] 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 <tseaver@palladion.com>
Date: Wed, 29 May 2013 16:38:55 -0400
Subject: [PATCH 13/73] 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 <tseaver@palladion.com>
Date: Wed, 29 May 2013 16:42:06 -0400
Subject: [PATCH 14/73] 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 <tseaver@palladion.com>
Date: Wed, 29 May 2013 16:45:10 -0400
Subject: [PATCH 15/73] 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 <tseaver@palladion.com>
Date: Wed, 29 May 2013 17:00:17 -0400
Subject: [PATCH 16/73] 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 <tseaver@palladion.com>
Date: Wed, 29 May 2013 18:20:56 -0400
Subject: [PATCH 17/73] 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 <tseaver@palladion.com>
Date: Wed, 29 May 2013 18:48:58 -0400
Subject: [PATCH 18/73] 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 <tseaver@palladion.com>
Date: Wed, 29 May 2013 18:56:54 -0400
Subject: [PATCH 19/73] 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 <tseaver@palladion.com>
Date: Tue, 4 Jun 2013 16:24:11 -0400
Subject: [PATCH 20/73] 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 <tseaver@palladion.com>
Date: Tue, 4 Jun 2013 19:37:51 -0400
Subject: [PATCH 21/73] 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 <tseaver@palladion.com>
Date: Wed, 5 Jun 2013 10:30:57 -0400
Subject: [PATCH 22/73] 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 <tseaver@palladion.com>
Date: Wed, 5 Jun 2013 10:31:19 -0400
Subject: [PATCH 23/73] 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 <tseaver@palladion.com>
Date: Wed, 5 Jun 2013 10:43:56 -0400
Subject: [PATCH 24/73] 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 <tseaver@palladion.com>
Date: Wed, 5 Jun 2013 10:46:53 -0400
Subject: [PATCH 25/73] 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 <tseaver@palladion.com>
Date: Wed, 5 Jun 2013 10:52:25 -0400
Subject: [PATCH 26/73] 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 <tseaver@palladion.com>
Date: Wed, 5 Jun 2013 10:52:57 -0400
Subject: [PATCH 27/73] 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 <tseaver@palladion.com>
Date: Wed, 5 Jun 2013 10:58:32 -0400
Subject: [PATCH 28/73] 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 <tseaver@palladion.com>
Date: Wed, 5 Jun 2013 11:05:41 -0400
Subject: [PATCH 29/73] 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 <tseaver@palladion.com>
Date: Wed, 5 Jun 2013 14:44:28 -0400
Subject: [PATCH 30/73] 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 <tseaver@palladion.com>
Date: Wed, 5 Jun 2013 14:45:27 -0400
Subject: [PATCH 31/73] 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.")
 

From b2e51e721625329db4ad6af4428950b89114e802 Mon Sep 17 00:00:00 2001
From: Eric Casteleijn <ec@trapit.com>
Date: Mon, 24 Mar 2014 14:56:50 -0700
Subject: [PATCH 32/73] make Request.to_url work with non-ascii parameters.
 issue #154

---
 oauth2/__init__.py  |  2 +-
 tests/test_oauth.py | 29 +++++++++++++++++++++++++++--
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 835270e3..98284c1a 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -420,7 +420,7 @@ def to_url(self):
             query = base_url[4]
         query = parse_qs(query)
         for k, v in self.items():
-            query.setdefault(k, []).append(v)
+            query.setdefault(k, []).append(to_utf8_optional_iterator(v))
         
         try:
             scheme = base_url.scheme
diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index 099e5794..9c648d6b 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -31,7 +31,6 @@
 import time
 import urllib
 import urlparse
-from types import ListType
 import mock
 import httplib2
 
@@ -42,7 +41,7 @@
     from cgi import parse_qs, parse_qsl
 
 
-sys.path[0:0] = [os.path.join(os.path.dirname(__file__), ".."),]
+sys.path[0:0] = [os.path.join(os.path.dirname(__file__), "..")]
 
 
 class TestError(unittest.TestCase):
@@ -375,6 +374,32 @@ def test_get_nonoauth_parameters(self):
         req = oauth.Request("GET", "http://example.com", params)
         self.assertEquals(other_params, req.get_nonoauth_parameters())
 
+    def test_to_url_works_with_non_ascii_parameters(self):
+
+        oauth_params = {
+            'oauth_consumer': 'asdfasdfasdf'
+        }
+
+        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'åÅøØ',
+        }
+
+        params = oauth_params
+        params.update(other_params)
+
+        req = oauth.Request("GET", "http://example.com", params)
+        self.assertEquals(
+            req.to_url(), 
+            'http://example.com?oauth_consumer=asdfasdfasdf&'
+            'uni_unicode_2=%C3%A5%C3%85%C3%B8%C3%98&'
+            'uni_utf8=%C2%AE&multi=%5B%27FOO%27%2C+%27BAR%27%5D&'
+            'uni_unicode=%C2%AE&bar=foo&foo=baz')
+
     def test_to_header(self):
         realm = "http://sp.example.com/"
 

From 7683c26d2697ab0a5af6e33d4f9e7084a454e3c4 Mon Sep 17 00:00:00 2001
From: Henry Cooke <me@prehensile.co.uk>
Date: Mon, 25 Aug 2014 22:41:08 +0100
Subject: [PATCH 33/73] amended Client class to pass **kwargs on to super

Allows more flexibility when instantiating a Client in passing arguments to the super httplib2.Http init. Using in my case because the remote server (flickr) has incorrectly configured SSL certificates, but could be general-purpose useful.
---
 oauth2/__init__.py | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 835270e3..525e6834 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -615,8 +615,7 @@ def _split_url_string(param_str):
 class Client(httplib2.Http):
     """OAuthClient is a worker to attempt to execute a request."""
 
-    def __init__(self, consumer, token=None, cache=None, timeout=None,
-        proxy_info=None):
+    def __init__(self, consumer, token=None, **kwargs):
 
         if consumer is not None and not isinstance(consumer, Consumer):
             raise ValueError("Invalid consumer.")
@@ -628,7 +627,7 @@ 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)
+        super(Client, self).__init__(**kwargs)
 
     def set_signature_method(self, method):
         if not isinstance(method, SignatureMethod):

From c6144dd8b6a357795e3e89f35fb1eb12dbf5bef4 Mon Sep 17 00:00:00 2001
From: Sebastien Piquemal <sebpiq@gmail.com>
Date: Thu, 13 Nov 2014 09:50:24 +0200
Subject: [PATCH 34/73] Added syntax highlighting in README examples

---
 README.md | 532 ++++++++++++++++++++++++++++--------------------------
 1 file changed, 272 insertions(+), 260 deletions(-)

diff --git a/README.md b/README.md
index 3dca1e34..866b4328 100644
--- a/README.md
+++ b/README.md
@@ -11,59 +11,63 @@ This code was originally forked from [Leah Culver and Andy Smith's oauth.py code
 
 # Signing a Request
 
-    import oauth2 as oauth
-    import time
-    
-    # Set the API endpoint 
-    url = "http://example.com/photos"
-    
-    # Set the base oauth_* parameters along with any other parameters required
-    # for the API call.
-    params = {
-        'oauth_version': "1.0",
-        'oauth_nonce': oauth.generate_nonce(),
-        'oauth_timestamp': int(time.time())
-        'user': 'joestump',
-        'photoid': 555555555555
-    }
-    
-    # Set up instances of our Token and Consumer. The Consumer.key and 
-    # Consumer.secret are given to you by the API provider. The Token.key and
-    # Token.secret is given to you after a three-legged authentication.
-    token = oauth.Token(key="tok-test-key", secret="tok-test-secret")
-    consumer = oauth.Consumer(key="con-test-key", secret="con-test-secret")
-    
-    # Set our token/key parameters
-    params['oauth_token'] = token.key
-    params['oauth_consumer_key'] = consumer.key
-    
-    # Create our request. Change method, etc. accordingly.
-    req = oauth.Request(method="GET", url=url, parameters=params)
-    
-    # Sign the request.
-    signature_method = oauth.SignatureMethod_HMAC_SHA1()
-    req.sign_request(signature_method, consumer, token)
+```python
+import oauth2 as oauth
+import time
+
+# Set the API endpoint 
+url = "http://example.com/photos"
+
+# Set the base oauth_* parameters along with any other parameters required
+# for the API call.
+params = {
+    'oauth_version': "1.0",
+    'oauth_nonce': oauth.generate_nonce(),
+    'oauth_timestamp': int(time.time())
+    'user': 'joestump',
+    'photoid': 555555555555
+}
+
+# Set up instances of our Token and Consumer. The Consumer.key and 
+# Consumer.secret are given to you by the API provider. The Token.key and
+# Token.secret is given to you after a three-legged authentication.
+token = oauth.Token(key="tok-test-key", secret="tok-test-secret")
+consumer = oauth.Consumer(key="con-test-key", secret="con-test-secret")
+
+# Set our token/key parameters
+params['oauth_token'] = token.key
+params['oauth_consumer_key'] = consumer.key
+
+# Create our request. Change method, etc. accordingly.
+req = oauth.Request(method="GET", url=url, parameters=params)
+
+# Sign the request.
+signature_method = oauth.SignatureMethod_HMAC_SHA1()
+req.sign_request(signature_method, consumer, token)
+```
 
 # Using the Client
 
 The <code>oauth2.Client</code> is based on <code>httplib2</code> and works just as you'd expect it to. The only difference is the first two arguments to the constructor are an instance of <code>oauth2.Consumer</code> and <code>oauth2.Token</code> (<code>oauth2.Token</code> is only needed for three-legged requests).
 
-    import oauth2 as oauth
-    
-    # Create your consumer with the proper key/secret.
-    consumer = oauth.Consumer(key="your-twitter-consumer-key", 
-        secret="your-twitter-consumer-secret")
-    
-    # Request token URL for Twitter.
-    request_token_url = "http://twitter.com/oauth/request_token"
-    
-    # Create our client.
-    client = oauth.Client(consumer)
-    
-    # The OAuth Client request works just like httplib2 for the most part.
-    resp, content = client.request(request_token_url, "GET")
-    print resp
-    print content
+```python
+import oauth2 as oauth
+
+# Create your consumer with the proper key/secret.
+consumer = oauth.Consumer(key="your-twitter-consumer-key", 
+    secret="your-twitter-consumer-secret")
+
+# Request token URL for Twitter.
+request_token_url = "http://twitter.com/oauth/request_token"
+
+# Create our client.
+client = oauth.Client(consumer)
+
+# The OAuth Client request works just like httplib2 for the most part.
+resp, content = client.request(request_token_url, "GET")
+print resp
+print content
+```
 
 # Twitter Three-legged OAuth Example
 
@@ -71,69 +75,71 @@ Below is an example of how one would go through a three-legged OAuth flow to
 gain access to protected resources on Twitter. This is a simple CLI script, but
 can be easily translated to a web application.
 
-    import urlparse
-    import oauth2 as oauth
-    
-    consumer_key = 'my_key_from_twitter'
-    consumer_secret = 'my_secret_from_twitter'
-    
-    request_token_url = 'http://twitter.com/oauth/request_token'
-    access_token_url = 'http://twitter.com/oauth/access_token'
-    authorize_url = 'http://twitter.com/oauth/authorize'
-    
-    consumer = oauth.Consumer(consumer_key, consumer_secret)
-    client = oauth.Client(consumer)
-    
-    # Step 1: Get a request token. This is a temporary token that is used for 
-    # having the user authorize an access token and to sign the request to obtain 
-    # said access token.
-    
-    resp, content = client.request(request_token_url, "GET")
-    if resp['status'] != '200':
-        raise Exception("Invalid response %s." % resp['status'])
-    
-    request_token = dict(urlparse.parse_qsl(content))
-    
-    print "Request Token:"
-    print "    - oauth_token        = %s" % request_token['oauth_token']
-    print "    - oauth_token_secret = %s" % request_token['oauth_token_secret']
-    print 
-    
-    # Step 2: Redirect to the provider. Since this is a CLI script we do not 
-    # redirect. In a web application you would redirect the user to the URL
-    # below.
-    
-    print "Go to the following link in your browser:"
-    print "%s?oauth_token=%s" % (authorize_url, request_token['oauth_token'])
-    print 
-    
-    # After the user has granted access to you, the consumer, the provider will
-    # redirect you to whatever URL you have told them to redirect to. You can 
-    # usually define this in the oauth_callback argument as well.
-    accepted = 'n'
-    while accepted.lower() == 'n':
-        accepted = raw_input('Have you authorized me? (y/n) ')
-    oauth_verifier = raw_input('What is the PIN? ')
-    
-    # Step 3: Once the consumer has redirected the user back to the oauth_callback
-    # URL you can request the access token the user has approved. You use the 
-    # request token to sign this request. After this is done you throw away the
-    # request token and use the access token returned. You should store this 
-    # access token somewhere safe, like a database, for future use.
-    token = oauth.Token(request_token['oauth_token'],
-        request_token['oauth_token_secret'])
-    token.set_verifier(oauth_verifier)
-    client = oauth.Client(consumer, token)
-    
-    resp, content = client.request(access_token_url, "POST")
-    access_token = dict(urlparse.parse_qsl(content))
+```python
+import urlparse
+import oauth2 as oauth
+
+consumer_key = 'my_key_from_twitter'
+consumer_secret = 'my_secret_from_twitter'
+
+request_token_url = 'http://twitter.com/oauth/request_token'
+access_token_url = 'http://twitter.com/oauth/access_token'
+authorize_url = 'http://twitter.com/oauth/authorize'
+
+consumer = oauth.Consumer(consumer_key, consumer_secret)
+client = oauth.Client(consumer)
+
+# Step 1: Get a request token. This is a temporary token that is used for 
+# having the user authorize an access token and to sign the request to obtain 
+# said access token.
     
-    print "Access Token:"
-    print "    - oauth_token        = %s" % access_token['oauth_token']
-    print "    - oauth_token_secret = %s" % access_token['oauth_token_secret']
-    print
-    print "You may now access protected resources using the access tokens above." 
-    print
+resp, content = client.request(request_token_url, "GET")
+if resp['status'] != '200':
+    raise Exception("Invalid response %s." % resp['status'])
+
+request_token = dict(urlparse.parse_qsl(content))
+
+print "Request Token:"
+print "    - oauth_token        = %s" % request_token['oauth_token']
+print "    - oauth_token_secret = %s" % request_token['oauth_token_secret']
+print 
+
+# Step 2: Redirect to the provider. Since this is a CLI script we do not 
+# redirect. In a web application you would redirect the user to the URL
+# below.
+
+print "Go to the following link in your browser:"
+print "%s?oauth_token=%s" % (authorize_url, request_token['oauth_token'])
+print 
+
+# After the user has granted access to you, the consumer, the provider will
+# redirect you to whatever URL you have told them to redirect to. You can 
+# usually define this in the oauth_callback argument as well.
+accepted = 'n'
+while accepted.lower() == 'n':
+    accepted = raw_input('Have you authorized me? (y/n) ')
+oauth_verifier = raw_input('What is the PIN? ')
+
+# Step 3: Once the consumer has redirected the user back to the oauth_callback
+# URL you can request the access token the user has approved. You use the 
+# request token to sign this request. After this is done you throw away the
+# request token and use the access token returned. You should store this 
+# access token somewhere safe, like a database, for future use.
+token = oauth.Token(request_token['oauth_token'],
+    request_token['oauth_token_secret'])
+token.set_verifier(oauth_verifier)
+client = oauth.Client(consumer, token)
+
+resp, content = client.request(access_token_url, "POST")
+access_token = dict(urlparse.parse_qsl(content))
+
+print "Access Token:"
+print "    - oauth_token        = %s" % access_token['oauth_token']
+print "    - oauth_token_secret = %s" % access_token['oauth_token_secret']
+print
+print "You may now access protected resources using the access tokens above." 
+print
+```
 
 # Logging into Django w/ Twitter
 
@@ -150,10 +156,12 @@ You'll need a place to store all of your Twitter OAuth credentials after the
 user has logged in. In your app's `models.py` file you should add something
 that resembles the following model.
 
-    class Profile(models.Model):
-        user = models.ForeignKey(User)
-        oauth_token = models.CharField(max_length=200)
-        oauth_secret = models.CharField(max_length=200)
+```python
+class Profile(models.Model):
+    user = models.ForeignKey(User)
+    oauth_token = models.CharField(max_length=200)
+    oauth_secret = models.CharField(max_length=200)
+```
 
 ## Set up your Django views
 
@@ -168,128 +176,130 @@ be logged in, `^login/authenticated/` and `twitter_authenticated` will confirm
 the login, create the account if necessary, and log the user into the 
 application, and `^logout`/ logs the user out in the `twitter_logout` view.
 
+```python
+from django.conf.urls.defaults import *
+from django.contrib import admin
+from mytwitterapp.views import twitter_login, twitter_logout, \
+    twitter_authenticated
 
-    from django.conf.urls.defaults import *
-    from django.contrib import admin
-    from mytwitterapp.views import twitter_login, twitter_logout, \
-        twitter_authenticated
+admin.autodiscover()
 
-    admin.autodiscover()
-
-    urlpatterns = patterns('',
-        url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
-        url(r'^admin/', include(admin.site.urls)),
-        url(r'^login/?$', twitter_login),
-        url(r'^logout/?$', twitter_logout),
-        url(r'^login/authenticated/?$', twitter_authenticated),
-    )
+urlpatterns = patterns('',
+    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
+    url(r'^admin/', include(admin.site.urls)),
+    url(r'^login/?$', twitter_login),
+    url(r'^logout/?$', twitter_logout),
+    url(r'^login/authenticated/?$', twitter_authenticated),
+)
+```
 
 ### `views.py`
 
 *NOTE:* The following code was coded for Python 2.4 so some of the libraries 
 and code here might need to be updated if you are using Python 2.6+. 
 
-    # Python
-    import oauth2 as oauth
-    import cgi
+```python
+# Python
+import oauth2 as oauth
+import cgi
 
-    # Django
-    from django.shortcuts import render_to_response
-    from django.http import HttpResponseRedirect
-    from django.conf import settings
-    from django.contrib.auth import authenticate, login, logout
-    from django.contrib.auth.models import User
-    from django.contrib.auth.decorators import login_required
+# Django
+from django.shortcuts import render_to_response
+from django.http import HttpResponseRedirect
+from django.conf import settings
+from django.contrib.auth import authenticate, login, logout
+from django.contrib.auth.models import User
+from django.contrib.auth.decorators import login_required
 
-    # Project
-    from mytwitterapp.models import Profile
+# Project
+from mytwitterapp.models import Profile
 
-    # It's probably a good idea to put your consumer's OAuth token and
-    # OAuth secret into your project's settings. 
-    consumer = oauth.Consumer(settings.TWITTER_TOKEN, settings.TWITTER_SECRET)
-    client = oauth.Client(consumer)
+# It's probably a good idea to put your consumer's OAuth token and
+# OAuth secret into your project's settings. 
+consumer = oauth.Consumer(settings.TWITTER_TOKEN, settings.TWITTER_SECRET)
+client = oauth.Client(consumer)
 
-    request_token_url = 'http://twitter.com/oauth/request_token'
-    access_token_url = 'http://twitter.com/oauth/access_token'
+request_token_url = 'http://twitter.com/oauth/request_token'
+access_token_url = 'http://twitter.com/oauth/access_token'
 
-    # This is the slightly different URL used to authenticate/authorize.
-    authenticate_url = 'http://twitter.com/oauth/authenticate'
+# This is the slightly different URL used to authenticate/authorize.
+authenticate_url = 'http://twitter.com/oauth/authenticate'
 
-    def twitter_login(request):
-        # Step 1. Get a request token from Twitter.
-        resp, content = client.request(request_token_url, "GET")
-        if resp['status'] != '200':
-            raise Exception("Invalid response from Twitter.")
+def twitter_login(request):
+    # Step 1. Get a request token from Twitter.
+    resp, content = client.request(request_token_url, "GET")
+    if resp['status'] != '200':
+        raise Exception("Invalid response from Twitter.")
 
-        # Step 2. Store the request token in a session for later use.
-        request.session['request_token'] = dict(cgi.parse_qsl(content))
+    # Step 2. Store the request token in a session for later use.
+    request.session['request_token'] = dict(cgi.parse_qsl(content))
 
-        # Step 3. Redirect the user to the authentication URL.
-        url = "%s?oauth_token=%s" % (authenticate_url,
-            request.session['request_token']['oauth_token'])
+    # Step 3. Redirect the user to the authentication URL.
+    url = "%s?oauth_token=%s" % (authenticate_url,
+        request.session['request_token']['oauth_token'])
 
-        return HttpResponseRedirect(url)
+    return HttpResponseRedirect(url)
 
     
-    @login_required
-    def twitter_logout(request):
-        # Log a user out using Django's logout function and redirect them
-        # back to the homepage.
-        logout(request)
-        return HttpResponseRedirect('/')
-
-    def twitter_authenticated(request):
-        # Step 1. Use the request token in the session to build a new client.
-        token = oauth.Token(request.session['request_token']['oauth_token'],
-            request.session['request_token']['oauth_token_secret'])
-        client = oauth.Client(consumer, token)
-    
-        # Step 2. Request the authorized access token from Twitter.
-        resp, content = client.request(access_token_url, "GET")
-        if resp['status'] != '200':
-            print content
-            raise Exception("Invalid response from Twitter.")
-    
-        """
-        This is what you'll get back from Twitter. Note that it includes the
-        user's user_id and screen_name.
-        {
-            'oauth_token_secret': 'IcJXPiJh8be3BjDWW50uCY31chyhsMHEhqJVsphC3M',
-            'user_id': '120889797', 
-            'oauth_token': '120889797-H5zNnM3qE0iFoTTpNEHIz3noL9FKzXiOxwtnyVOD',
-            'screen_name': 'heyismysiteup'
-        }
-        """
-        access_token = dict(cgi.parse_qsl(content))
-    
-        # Step 3. Lookup the user or create them if they don't exist.
-        try:
-            user = User.objects.get(username=access_token['screen_name'])
-        except User.DoesNotExist:
-            # When creating the user I just use their screen_name@twitter.com
-            # for their email and the oauth_token_secret for their password.
-            # These two things will likely never be used. Alternatively, you 
-            # can prompt them for their email here. Either way, the password 
-            # should never be used.
-            user = User.objects.create_user(access_token['screen_name'],
-                '%s@twitter.com' % access_token['screen_name'],
-                access_token['oauth_token_secret'])
-    
-            # Save our permanent token and secret for later.
-            profile = Profile()
-            profile.user = user
-            profile.oauth_token = access_token['oauth_token']
-            profile.oauth_secret = access_token['oauth_token_secret']
-            profile.save()
-    
-        # Authenticate the user and log them in using Django's pre-built 
-        # functions for these things.
-        user = authenticate(username=access_token['screen_name'],
-            password=access_token['oauth_token_secret'])
-        login(request, user)
-    
-        return HttpResponseRedirect('/')
+@login_required
+def twitter_logout(request):
+    # Log a user out using Django's logout function and redirect them
+    # back to the homepage.
+    logout(request)
+    return HttpResponseRedirect('/')
+
+def twitter_authenticated(request):
+    # Step 1. Use the request token in the session to build a new client.
+    token = oauth.Token(request.session['request_token']['oauth_token'],
+        request.session['request_token']['oauth_token_secret'])
+    client = oauth.Client(consumer, token)
+
+    # Step 2. Request the authorized access token from Twitter.
+    resp, content = client.request(access_token_url, "GET")
+    if resp['status'] != '200':
+        print content
+        raise Exception("Invalid response from Twitter.")
+
+    """
+    This is what you'll get back from Twitter. Note that it includes the
+    user's user_id and screen_name.
+    {
+        'oauth_token_secret': 'IcJXPiJh8be3BjDWW50uCY31chyhsMHEhqJVsphC3M',
+        'user_id': '120889797', 
+        'oauth_token': '120889797-H5zNnM3qE0iFoTTpNEHIz3noL9FKzXiOxwtnyVOD',
+        'screen_name': 'heyismysiteup'
+    }
+    """
+    access_token = dict(cgi.parse_qsl(content))
+
+    # Step 3. Lookup the user or create them if they don't exist.
+    try:
+        user = User.objects.get(username=access_token['screen_name'])
+    except User.DoesNotExist:
+        # When creating the user I just use their screen_name@twitter.com
+        # for their email and the oauth_token_secret for their password.
+        # These two things will likely never be used. Alternatively, you 
+        # can prompt them for their email here. Either way, the password 
+        # should never be used.
+        user = User.objects.create_user(access_token['screen_name'],
+            '%s@twitter.com' % access_token['screen_name'],
+            access_token['oauth_token_secret'])
+
+        # Save our permanent token and secret for later.
+        profile = Profile()
+        profile.user = user
+        profile.oauth_token = access_token['oauth_token']
+        profile.oauth_secret = access_token['oauth_token_secret']
+        profile.save()
     
+    # Authenticate the user and log them in using Django's pre-built 
+    # functions for these things.
+    user = authenticate(username=access_token['screen_name'],
+        password=access_token['oauth_token_secret'])
+    login(request, user)
+
+    return HttpResponseRedirect('/')
+```
 
 ### `settings.py`
 
@@ -302,56 +312,58 @@ Gmail supports OAuth over IMAP and SMTP via a standard they call XOAUTH. This al
 
 ## IMAP
 
-    import oauth2 as oauth
-    import oauth2.clients.imap as imaplib
+```python
+import oauth2 as oauth
+import oauth2.clients.imap as imaplib
 
-    # Set up your Consumer and Token as per usual. Just like any other
-    # three-legged OAuth request.
-    consumer = oauth.Consumer('your_consumer_key', 'your_consumer_secret')
-    token = oauth.Token('your_users_3_legged_token', 
-        'your_users_3_legged_token_secret')
+# Set up your Consumer and Token as per usual. Just like any other
+# three-legged OAuth request.
+consumer = oauth.Consumer('your_consumer_key', 'your_consumer_secret')
+token = oauth.Token('your_users_3_legged_token', 
+    'your_users_3_legged_token_secret')
 
-    # Setup the URL according to Google's XOAUTH implementation. Be sure
-    # to replace the email here with the appropriate email address that
-    # you wish to access.
-    url = "https://mail.google.com/mail/b/your_users_email@gmail.com/imap/"
+# Setup the URL according to Google's XOAUTH implementation. Be sure
+# to replace the email here with the appropriate email address that
+# you wish to access.
+url = "https://mail.google.com/mail/b/your_users_email@gmail.com/imap/"
 
-    conn = imaplib.IMAP4_SSL('imap.googlemail.com')
-    conn.debug = 4 
+conn = imaplib.IMAP4_SSL('imap.googlemail.com')
+conn.debug = 4 
 
-    # This is the only thing in the API for impaplib.IMAP4_SSL that has 
-    # changed. You now authenticate with the URL, consumer, and token.
-    conn.authenticate(url, consumer, token)
-
-    # Once authenticated everything from the impalib.IMAP4_SSL class will 
-    # work as per usual without any modification to your code.
-    conn.select('INBOX')
-    print conn.list()
+# This is the only thing in the API for impaplib.IMAP4_SSL that has 
+# changed. You now authenticate with the URL, consumer, and token.
+conn.authenticate(url, consumer, token)
 
+# Once authenticated everything from the impalib.IMAP4_SSL class will 
+# work as per usual without any modification to your code.
+conn.select('INBOX')
+print conn.list()
+```
 
 ## SMTP
 
-    import oauth2 as oauth
-    import oauth2.clients.smtp as smtplib
-
-    # Set up your Consumer and Token as per usual. Just like any other
-    # three-legged OAuth request.
-    consumer = oauth.Consumer('your_consumer_key', 'your_consumer_secret')
-    token = oauth.Token('your_users_3_legged_token', 
-        'your_users_3_legged_token_secret')
-
-    # Setup the URL according to Google's XOAUTH implementation. Be sure
-    # to replace the email here with the appropriate email address that
-    # you wish to access.
-    url = "https://mail.google.com/mail/b/your_users_email@gmail.com/smtp/"
-
-    conn = smtplib.SMTP('smtp.googlemail.com', 587)
-    conn.set_debuglevel(True)
-    conn.ehlo('test')
-    conn.starttls()
-
-    # Again the only thing modified from smtplib.SMTP is the authenticate
-    # method, which works identically to the imaplib.IMAP4_SSL method.
-    conn.authenticate(url, consumer, token)
-
+```python
+import oauth2 as oauth
+import oauth2.clients.smtp as smtplib
+
+# Set up your Consumer and Token as per usual. Just like any other
+# three-legged OAuth request.
+consumer = oauth.Consumer('your_consumer_key', 'your_consumer_secret')
+token = oauth.Token('your_users_3_legged_token', 
+    'your_users_3_legged_token_secret')
+
+# Setup the URL according to Google's XOAUTH implementation. Be sure
+# to replace the email here with the appropriate email address that
+# you wish to access.
+url = "https://mail.google.com/mail/b/your_users_email@gmail.com/smtp/"
+
+conn = smtplib.SMTP('smtp.googlemail.com', 587)
+conn.set_debuglevel(True)
+conn.ehlo('test')
+conn.starttls()
+
+# Again the only thing modified from smtplib.SMTP is the authenticate
+# method, which works identically to the imaplib.IMAP4_SSL method.
+conn.authenticate(url, consumer, token)
+```
 

From 800bea04d29115c77e7d8800815df5bf39c2bac7 Mon Sep 17 00:00:00 2001
From: Joe Stump <joe@joestump.net>
Date: Tue, 28 Jul 2015 19:17:39 -0700
Subject: [PATCH 35/73] Removed the Debian directory; should be maintained by
 CI. Fixed a typo. Closes #161.

---
 README.md         |   2 +-
 debian/changelog  | 408 ----------------------------------------------
 debian/compat     |   1 -
 debian/control    |  24 ---
 debian/copyright  |  21 ---
 debian/pycompat   |   1 -
 debian/pyversions |   1 -
 debian/rules      |  12 --
 8 files changed, 1 insertion(+), 469 deletions(-)
 delete mode 100644 debian/changelog
 delete mode 100644 debian/compat
 delete mode 100644 debian/control
 delete mode 100644 debian/copyright
 delete mode 100644 debian/pycompat
 delete mode 100644 debian/pyversions
 delete mode 100755 debian/rules

diff --git a/README.md b/README.md
index 866b4328..0f49c607 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,7 @@ url = "http://example.com/photos"
 params = {
     'oauth_version': "1.0",
     'oauth_nonce': oauth.generate_nonce(),
-    'oauth_timestamp': int(time.time())
+    'oauth_timestamp': int(time.time()),
     'user': 'joestump',
     'photoid': 555555555555
 }
diff --git a/debian/changelog b/debian/changelog
deleted file mode 100644
index 7a462a73..00000000
--- a/debian/changelog
+++ /dev/null
@@ -1,408 +0,0 @@
-python-oauth2 (1.5.211) maverick; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Tue, 13 Dec 2011 17:39:07 +0000
-
-python-oauth2 (1.5.210) maverick; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Fri, 09 Dec 2011 01:17:31 +0000
-
-python-oauth2 (1.5.170) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 02 May 2011 23:48:16 +0000
-
-python-oauth2 (1.5.169) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Fri, 15 Apr 2011 21:29:41 +0000
-
-python-oauth2 (1.5.168) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Tue, 05 Apr 2011 23:13:18 +0000
-
-python-oauth2 (1.5.167) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Thu, 17 Feb 2011 21:56:49 +0000
-
-python-oauth2 (1.5.166) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * fix encoding of non-ascii data into postdata, really fixes #48 this
-    time (comes with test)
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Tue, 15 Feb 2011 17:23:00 +0000
-
-python-oauth2 (1.5.165) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * get_normalized_parameters() is required to exclude any
-    oauth_signature that was already there, and it is required to
-    preserve duplicate keys, even with identical values ref #46, fixes
-    pull/49, thanks @zyegfryed
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 07 Feb 2011 19:24:56 +0000
-
-python-oauth2 (1.5.164) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * make it actually inspect the request, and fix the test to require
-    the right things of the request ref #46, ref pull/49, thanks
-    @zyegfryed
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 07 Feb 2011 18:53:56 +0000
-
-python-oauth2 (1.5.163) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * add test of #46; fixes #46
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Fri, 04 Feb 2011 18:14:56 +0000
-
-python-oauth2 (1.5.162) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * import parse_qs from cgi if it can't be imported from urlparse, thus
-    regaining compatibility with Python 2.5 fixes #47 thanks to @vtsao,
-    @subsume, @worksology
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Fri, 04 Feb 2011 17:50:56 +0000
-
-python-oauth2 (1.5.161) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * Include all parameters from URL, even ones that begin with "oauth_",
-    in signature base. effectively reverts
-    https://github.com/simplegeo/python-
-    oauth2/commit/50ca9578f598faff5427e58814e2f45d01d3261c fixes #27
-    Thanks to @robhudson for the bug report and help debugging.
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Thu, 03 Feb 2011 18:03:59 +0000
-
-python-oauth2 (1.5.160) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * add another unicode test case to the tests just to show to user
-    saevarom on github
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Thu, 03 Feb 2011 00:01:55 +0000
-
-python-oauth2 (1.5.159) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Thu, 03 Feb 2011 00:00:04 +0000
-
-python-oauth2 (1.5.158) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * keep empty params parsed from the URL (fixes #22)
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Wed, 02 Feb 2011 19:34:25 +0000
-
-python-oauth2 (1.5.155) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * use multiple values under the same key from the body (fixes #23)
-    Thanks to rogerm, ideasculptor, jiivan, timgraham.
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Tue, 01 Feb 2011 23:43:56 +0000
-
-python-oauth2 (1.5.154) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Tue, 01 Feb 2011 18:03:58 +0000
-
-python-oauth2 (1.5.153) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * add work-around for bug unicode-handling in urlparse in Python <
-    2.7, including tests
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Tue, 01 Feb 2011 16:31:56 +0000
-
-python-oauth2 (1.5.152) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * another test for handling of non-ascii, non-utf-8 urls
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Fri, 28 Jan 2011 18:14:34 +0000
-
-python-oauth2 (1.5.151) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Thu, 27 Jan 2011 05:31:55 +0000
-
-python-oauth2 (1.5.150) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Wed, 26 Jan 2011 17:15:25 +0000
-
-python-oauth2 (1.5.149) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * add the body-hash header as specified in
-    http://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-
-    bodyhash.html
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Wed, 26 Jan 2011 17:11:34 +0000
-
-python-oauth2 (1.5.148) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * default body is the empty string rather than None; add the Realm
-    header
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Wed, 26 Jan 2011 17:02:34 +0000
-
-python-oauth2 (1.5.147) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * pass the body and the is_form_encoding flag in to the Request object
-    (for future use in implementing the hash-of-body extension)
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Tue, 25 Jan 2011 17:39:34 +0000
-
-python-oauth2 (1.5.146) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * refactor internals of the way Client.request() decides whether this
-    is a x-www-form-urlencoded request or not
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Tue, 25 Jan 2011 17:30:38 +0000
-
-python-oauth2 (1.5.145) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * update version number from 1.4 to 1.5
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 24 Jan 2011 22:08:36 +0000
-
-python-oauth2 (1.4.144) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * more thorough unit tests for non-ascii urls, params, and query-args-
-    in-url
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 24 Jan 2011 22:02:34 +0000
-
-python-oauth2 (1.4.143) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * use two-part version number -- version is manually modified, build-
-    number is automatically modified
-
-  [ Simple Geebus ]
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 24 Jan 2011 20:21:20 +0000
-
-python-oauth2 (1.4.134) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 24 Jan 2011 18:30:58 +0000
-
-python-oauth2 (1.4.133) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * handle unicode objects and utf-8 strings in url and params and
-    encode them to utf-8 when serializing
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 24 Jan 2011 18:25:54 +0000
-
-python-oauth2 (1.4.132) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 24 Jan 2011 17:39:52 +0000
-
-python-oauth2 (1.4.131) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 24 Jan 2011 17:32:18 +0000
-
-python-oauth2 (1.4.130) lucid; urgency=low
-
-  * UNRELEASED
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 24 Jan 2011 17:22:46 +0000
-
-python-oauth2 (1.4.129) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * change manually-managed version number from 1.3 to 1.4
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Fri, 21 Jan 2011 19:52:32 +0000
-
-python-oauth2 (1.3.128) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * raise an exception if the URL passed in is not a unicode object nor
-    ascii We can't submit a correct URL with arbitrary bytes -- we have
-    to submit a utf-8 encoded unicode string. (Otherwise we'll cause
-    either a rejection or a signature mismatch in the server, which is
-    what has happened at SimpleGeo.) If the caller passes in non-ascii
-    things in a str then it would be better for them to change their
-    code to decode it to unicode before passing it in than for us to
-    decode it, since they have a better chance of knowing what encoding
-    it is in -- if we did it we would be guessing.
-  * fix flaws pointed out by ieure, plus test failures I'm not entirely
-    sure it is appropriate to allow .url and .normalized_url to be non-
-    existent in addition to allowing them to be None, but I don't really
-    understand the intent of the url setter.
-  * Oh, after we've made sure it can be converted to unicode, just leave
-    it as a unicode object after that.
-  * raise more specific error message if argument to to_unicode() is
-    neither unicode nor str
-  * a couple of cleanups suggested by ieure in code review
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Mon, 10 Jan 2011 22:42:35 +0000
-
-python-oauth2 (1.3.127) lucid; urgency=low
-
-  [ Zooko Ofsimplegeo ]
-  * run tests when building
-  * raise exception from a test just to see if hudson is *really*
-    running these tests. I don't see the test results.
-  * put version number in a file where it can be read and written by
-    various things (including hudson)
-  * make oauth2.__version__ exist, rename VERSION to OAUTH_VERSION for
-    clarity
-  * debian/control -- build-dep pyflakes
-  * fix pyflakes
-  * build-dep requires python-coverage
-  * switch from nosetests to the builtin test runner Just because it
-    doesn't seem to currently use any feature of nosetest and this way
-    we can reduce the dependencies by one. We'll feel free to switch it
-    back to using nosetests (and add the dep to debian/control this
-    time) if we want to use some extra feature of nose.
-  * remove test failure which was inserted just to see if hudson was
-    running the tests properly
-  * Fix bug inserted by pyflakes inspired cleanup earlier -- it turns
-    out that _get_version() wasn't dead code but was having a necessary
-    side-effect. Add new method named _check_version() to be clearer
-    about what it does compared to _get_version()
-  * port tests from mox to mock because they were failing and I don't
-    understand mox. now they are working. add mock to the python and
-    debian deps. remove apparently left-over line of code that runs
-    "sign()" at the end of checking a signature
-
-  [ SimpleGeo Nerds ]
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Tue, 04 Jan 2011 19:39:34 +0000
-
-python-oauth2 (1.2.1) lucid; urgency=low
-
-  [ Joe Stump ]
-  * Fixed a minor spelling error, added tests for
-    oauth2.Error.__str__(), and added a test for build_xoauth_string().
-  * Added a 'make test' command to the Makefile.
-  * Added a bunch of new tests. 100% coverage has lapsed a bit. Fixing
-    now.
-
-  [ Ian Eure ]
-  * Update deb rule in makefile to sign packages and move them to
-    dist/deb.
-
-  [ Joe Stump ]
-  * Implemented a 'fix' for a bug in certain OAuth providers.
-    http://bit.ly/aUrH43
-
-  [ Ian Eure ]
-  * Update packaging.
-  * Fix version disparity.
-
-  [ coulix ]
-  * get normalised parameters should not take into account oauth_ params
-
-  [ Mathias Herberts ]
-  * Modified get_normalized_parameters so it does not encode '~' for
-    full conformance with RFC 5849 section 3.6
-
-  [ Peter Bengtsson ]
-  * made it python 2.4 compatible
-
-  [ Roderic Campbell ]
-  * tok != token. con != consumer. May be confusing for people not
-    actually reading the code.
-
-  [ Ian Eure ]
-
- -- Ian Eure <ian@simplegeo.com>  Mon, 28 Jun 2010 12:02:02 -0700
-
-python-oauth2 (1.2.1pre2) unstable; urgency=low
-
-  * Fix version disparity between setup.py & debian/changelog.
-
- -- Ian Eure <ian@simplegeo.com>  Fri, 11 Jun 2010 16:11:41 -0700
-
-python-oauth2 (1.2.1pre1) unstable; urgency=low
-
-  * Make a native package.
-  * Increment version.
-  * Make debian/rules executable.
-  * Update standards-version, maintainers.
-  * Remvoe unneeded python-central build dependency.
-
- -- Ian Eure <ian@simplegeo.com>  Fri, 11 Jun 2010 16:09:01 -0700
-
-python-oauth2 (1.0.0-1simplegeo01) karmic; urgency=low
-
-  * Initial build for SimpleGeo
-
- -- SimpleGeo Nerds <nerds@simplegeo.com>  Wed, 21 Oct 2009 23:24:00 -0700
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index 7ed6ff82..00000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-5
diff --git a/debian/control b/debian/control
deleted file mode 100644
index de0d42fa..00000000
--- a/debian/control
+++ /dev/null
@@ -1,24 +0,0 @@
-Source: python-oauth2
-Section: python
-Priority: optional
-Maintainer: SimpleGeo Nerds <nerds@simplegeo.com>
-Uploaders: Chris Lea <chris.lea@gmail.com>, Ian Eure <ian@simplegeo.com>
-Standards-Version: 3.8.4
-XS-Python-Version: all
-Build-Depends: debhelper (>= 4.1.13), cdbs (>= 0.4.49), python, python-setuptools, python-support, pyflakes, python-coverage (>= 2.85), python-mock
-Homepage: http://github.com/simplegeo/python-oauth2
-
-Package: python-oauth2
-Architecture: all
-XB-Python-Version: ${python:Versions}
-Depends: ${python:Depends}, python-httplib2
-Provides: ${python:Provides}
-Suggests:
-Description: A Python OAuth class with several useful
- features.
- .
- * 100% unit test coverage.
- * The Request class now extends from dict.
- * The Client class works and extends from httplib2.
-   It's a thin wrapper that handles automatically signing
-   any normal HTTP request you might wish to make.
diff --git a/debian/copyright b/debian/copyright
deleted file mode 100644
index 8f5e8bda..00000000
--- a/debian/copyright
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License
-
-Copyright (c) 2007 Leah Culver
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
\ No newline at end of file
diff --git a/debian/pycompat b/debian/pycompat
deleted file mode 100644
index 0cfbf088..00000000
--- a/debian/pycompat
+++ /dev/null
@@ -1 +0,0 @@
-2
diff --git a/debian/pyversions b/debian/pyversions
deleted file mode 100644
index b3dc41eb..00000000
--- a/debian/pyversions
+++ /dev/null
@@ -1 +0,0 @@
-2.5-
diff --git a/debian/rules b/debian/rules
deleted file mode 100755
index 4608bb84..00000000
--- a/debian/rules
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/usr/bin/make -f
-# -*- mode: makefile; coding: utf-8 -*-
-
-DEB_PYTHON_SYSTEM = pysupport
-
-include /usr/share/cdbs/1/rules/debhelper.mk
-include /usr/share/cdbs/1/class/python-distutils.mk
-
-build/python-oauth2::
-	pyflakes oauth2
-	python-coverage run --branch --include=oauth2/* setup.py test
-	python-coverage html -d $(ARTIFACTS)/htmlcov

From a3ea60b7065f3182e8bc841905c09e587fca4910 Mon Sep 17 00:00:00 2001
From: Joe Stump <joe@joestump.net>
Date: Tue, 28 Jul 2015 19:24:44 -0700
Subject: [PATCH 36/73] Adding Travis CI to run tests.

---
 .travis.yml      | 5 +++++
 requirements.txt | 4 ++++
 2 files changed, 9 insertions(+)
 create mode 100644 .travis.yml
 create mode 100644 requirements.txt

diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..045affe4
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,5 @@
+language: python
+python:
+  - "2.7"
+install: "pip install -r requirements.txt"
+script: py.test 
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 00000000..f65e7d36
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+httplib2==0.9.1
+mock==1.3.0
+pep8==1.6.2
+pytest==2.7.2

From c57cb296347f890466800bd0bb1c3b1b4d2415b0 Mon Sep 17 00:00:00 2001
From: Joe Stump <joe@joestump.net>
Date: Tue, 28 Jul 2015 19:26:14 -0700
Subject: [PATCH 37/73] Build for 2.6 as well.

---
 .travis.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.travis.yml b/.travis.yml
index 045affe4..7796bb8a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,6 @@
 language: python
 python:
+  - "2.6"
   - "2.7"
 install: "pip install -r requirements.txt"
 script: py.test 

From 25f46b840a3ae58a91c794990f4aa7e55159c5c2 Mon Sep 17 00:00:00 2001
From: Joe Stump <joe@joestump.net>
Date: Tue, 28 Jul 2015 19:26:57 -0700
Subject: [PATCH 38/73] Added build status badge.

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index 0f49c607..f44e74e1 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[![Build Status](https://travis-ci.org/joestump/python-oauth2.svg)](https://travis-ci.org/joestump/python-oauth2)
+
 # Overview
 
 This code was originally forked from [Leah Culver and Andy Smith's oauth.py code](http://github.com/leah/python-oauth/). Some of the tests come from a [fork by Vic Fryzel](http://github.com/shellsage/python-oauth), while a revamped Request class and more tests were merged in from [Mark Paschal's fork](http://github.com/markpasc/python-oauth). A number of notable differences exist between this code and its forefathers:

From 82dd2cdd4954cd7b8983d5d64c0dfd9072bf4650 Mon Sep 17 00:00:00 2001
From: Joe Stump <joe@joestump.net>
Date: Tue, 28 Jul 2015 19:55:33 -0700
Subject: [PATCH 39/73] Switched to SystemRandom() and updated README w/
 correct Twitter links. Fixes CVE-2013-4347. Closes #146. Closes #137.

---
 README.md          | 17 +++++++++--------
 oauth2/__init__.py |  6 +++---
 2 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/README.md b/README.md
index f44e74e1..a8094514 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ consumer = oauth.Consumer(key="your-twitter-consumer-key",
     secret="your-twitter-consumer-secret")
 
 # Request token URL for Twitter.
-request_token_url = "http://twitter.com/oauth/request_token"
+request_token_url = "https://api.twitter.com/oauth/request_token"
 
 # Create our client.
 client = oauth.Client(consumer)
@@ -84,9 +84,9 @@ import oauth2 as oauth
 consumer_key = 'my_key_from_twitter'
 consumer_secret = 'my_secret_from_twitter'
 
-request_token_url = 'http://twitter.com/oauth/request_token'
-access_token_url = 'http://twitter.com/oauth/access_token'
-authorize_url = 'http://twitter.com/oauth/authorize'
+request_token_url = 'https://api.twitter.com/oauth/request_token'
+access_token_url = 'https://api.twitter.com/oauth/access_token'
+authorize_url = 'https://api.twitter.com/oauth/authorize'
 
 consumer = oauth.Consumer(consumer_key, consumer_secret)
 client = oauth.Client(consumer)
@@ -145,7 +145,7 @@ print
 
 # Logging into Django w/ Twitter
 
-Twitter also has the ability to authenticate a user [via an OAuth flow](http://apiwiki.twitter.com/Sign-in-with-Twitter). This
+Twitter also has the ability to authenticate a user [via an OAuth flow](https://dev.twitter.com/docs/auth/sign-twitter). This
 flow is exactly like the three-legged OAuth flow, except you send them to a 
 slightly different URL to authorize them. 
 
@@ -221,11 +221,11 @@ from mytwitterapp.models import Profile
 consumer = oauth.Consumer(settings.TWITTER_TOKEN, settings.TWITTER_SECRET)
 client = oauth.Client(consumer)
 
-request_token_url = 'http://twitter.com/oauth/request_token'
-access_token_url = 'http://twitter.com/oauth/access_token'
+request_token_url = 'https://api.twitter.com/oauth/request_token'
+access_token_url = 'https://api.twitter.com/oauth/access_token'
 
 # This is the slightly different URL used to authenticate/authorize.
-authenticate_url = 'http://twitter.com/oauth/authenticate'
+authenticate_url = 'https://api.twitter.com/oauth/authenticate'
 
 def twitter_login(request):
     # Step 1. Get a request token from Twitter.
@@ -254,6 +254,7 @@ def twitter_authenticated(request):
     # Step 1. Use the request token in the session to build a new client.
     token = oauth.Token(request.session['request_token']['oauth_token'],
         request.session['request_token']['oauth_token_secret'])
+    token.set_verifier(request.GET['oauth_verifier'])
     client = oauth.Client(consumer, token)
 
     # Step 2. Request the authorized access token from Twitter.
diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 835270e3..6bf49153 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -164,12 +164,12 @@ def generate_timestamp():
 
 def generate_nonce(length=8):
     """Generate pseudorandom number."""
-    return ''.join([str(random.randint(0, 9)) for i in range(length)])
+    return ''.join([str(random.SystemRandom().randint(0, 9)) for i in range(length)])
 
 
 def generate_verifier(length=8):
     """Generate pseudorandom number."""
-    return ''.join([str(random.randint(0, 9)) for i in range(length)])
+    return ''.join([str(random.SystemRandom().randint(0, 9)) for i in range(length)])
 
 
 class Consumer(object):
@@ -509,7 +509,7 @@ def make_timestamp(cls):
     @classmethod
     def make_nonce(cls):
         """Generate pseudorandom number."""
-        return str(random.randint(0, 100000000))
+        return str(random.SystemRandom().randint(0, 100000000))
  
     @classmethod
     def from_request(cls, http_method, http_url, headers=None, parameters=None,

From a1f0bb48032cdf13c94a3c3222c7430d8e297a40 Mon Sep 17 00:00:00 2001
From: Joe Stump <joe@joestump.net>
Date: Tue, 28 Jul 2015 20:09:13 -0700
Subject: [PATCH 40/73] Added the test for httplib2 kwargs. Fixes #136.

---
 tests/test_oauth.py | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index 9c648d6b..e5dffc6c 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -1218,6 +1218,18 @@ class Blah():
         except ValueError:
             pass
 
+    def test_init_passes_kwargs_to_httplib2(self):
+        class Blah():
+            pass
+ 
+        consumer = oauth.Consumer('token', 'secret')
+ 
+        # httplib2 options
+        client = oauth.Client(consumer, None, cache='.cache', timeout=3, disable_ssl_certificate_validation=True)
+        self.assertNotEquals(client.cache, None)
+        self.assertEquals(client.timeout, 3)
+
+
     def test_access_token_get(self):
         """Test getting an access token via GET."""
         client = oauth.Client(self.consumer, None)

From 7d0b8d7f9203f0ac0c4a9445d839844bec43478a Mon Sep 17 00:00:00 2001
From: Joe Stump <joe@joestump.net>
Date: Tue, 28 Jul 2015 21:11:19 -0700
Subject: [PATCH 41/73] Closes #91. Added robust UTF-8 support to
 Request.to_url().

---
 oauth2/__init__.py  | 26 +++++++++++++-------------
 tests/test_oauth.py | 27 +++++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 26452b6a..6b456b04 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -418,24 +418,24 @@ def to_url(self):
         except AttributeError:
             # must be python <2.5
             query = base_url[4]
-        query = parse_qs(query)
+        query = parse_qs(to_utf8(query))
         for k, v in self.items():
-            query.setdefault(k, []).append(to_utf8_optional_iterator(v))
+            query.setdefault(to_utf8(k), []).append(to_utf8_optional_iterator(v))
         
         try:
-            scheme = base_url.scheme
-            netloc = base_url.netloc
-            path = base_url.path
-            params = base_url.params
-            fragment = base_url.fragment
+            scheme = to_utf8(base_url.scheme)
+            netloc = to_utf8(base_url.netloc)
+            path = to_utf8(base_url.path)
+            params = to_utf8(base_url.params)
+            fragment = to_utf8(base_url.fragment)
         except AttributeError:
             # must be python <2.5
-            scheme = base_url[0]
-            netloc = base_url[1]
-            path = base_url[2]
-            params = base_url[3]
-            fragment = base_url[5]
-        
+            scheme = to_utf8(base_url[0])
+            netloc = to_utf8(base_url[1])
+            path = to_utf8(base_url[2])
+            params = to_utf8(base_url[3])
+            fragment = to_utf8(base_url[5])
+
         url = (scheme, netloc, path, params,
                urllib.urlencode(query, True), fragment)
         return urlparse.urlunparse(url)
diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index e5dffc6c..cc38275c 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -374,6 +374,33 @@ def test_get_nonoauth_parameters(self):
         req = oauth.Request("GET", "http://example.com", params)
         self.assertEquals(other_params, req.get_nonoauth_parameters())
 
+    def test_to_url_nonascii(self):
+        url = "http://sp.example.com/"
+
+        params = {
+            'nonasciithing': u'q\xbfu\xe9 ,aasp u?..a.s',
+            'oauth_version': "1.0",
+            'oauth_nonce': "4572616e48616d6d65724c61686176",
+            'oauth_timestamp': "137131200",
+            'oauth_consumer_key': "0685bd9184jfhq22",
+            'oauth_signature_method': "HMAC-SHA1",
+            'oauth_token': "ad180jjd733klru7",
+            'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",
+        }
+
+        req = oauth.Request("GET", url, params)
+        res = urlparse.urlparse(req.to_url())
+
+        params['nonasciithing'] = params['nonasciithing'].encode('utf-8')
+        exp = urlparse.urlparse("%s?%s" % (url, urllib.urlencode(params)))
+
+        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)
+
     def test_to_url_works_with_non_ascii_parameters(self):
 
         oauth_params = {

From 6205e682b60ad4ebc1f3abb8a978155d1d538f75 Mon Sep 17 00:00:00 2001
From: Joe Stump <joe@joestump.net>
Date: Tue, 28 Jul 2015 21:56:09 -0700
Subject: [PATCH 42/73] Use cls passed so people can override Request. Closes
 #107.

---
 oauth2/__init__.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 889d3531..c7193ea4 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -569,8 +569,8 @@ def from_consumer_and_token(cls, consumer, token=None,
             if token.verifier:
                 parameters['oauth_verifier'] = token.verifier
  
-        return Request(http_method, http_url, parameters, body=body, 
-                       is_form_encoded=is_form_encoded)
+        return cls(http_method, http_url, parameters, body=body, 
+            is_form_encoded=is_form_encoded)
  
     @classmethod
     def from_token_and_callback(cls, token, callback=None, 

From e4032cd5a7c6bb955481fe37d700d718d6f6284b Mon Sep 17 00:00:00 2001
From: Joe Stump <joe@joestump.net>
Date: Tue, 28 Jul 2015 22:04:41 -0700
Subject: [PATCH 43/73] Added notes on how to install and a link to the PIP
 package.

---
 README.md | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/README.md b/README.md
index a8094514..5855e208 100644
--- a/README.md
+++ b/README.md
@@ -11,6 +11,14 @@ This code was originally forked from [Leah Culver and Andy Smith's oauth.py code
 * The library is likely no longer compatible with Python 2.3.
 * The <code>Client</code> class works and extends from <code>httplib2</code>. It's a thin wrapper that handles automatically signing any normal HTTP request you might wish to make.
 
+# Installing
+
+You can install `oauth2` via [the PIP package](https://pypi.python.org/pypi/oauth2). 
+
+    $ pip install oauth2
+    
+We recommend using [virtualenv](https://virtualenv.pypa.io/en/latest/).
+
 # Signing a Request
 
 ```python

From f5a23c855ff20970782a6bd59f979f1a29538bc6 Mon Sep 17 00:00:00 2001
From: Joe Stump <joe@joestump.net>
Date: Tue, 28 Jul 2015 22:20:10 -0700
Subject: [PATCH 44/73] Move away from find_packages() to explicitly declaring
 the package.

---
 setup.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/setup.py b/setup.py
index acc41e17..78119d69 100755
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,5 @@
 #!/usr/bin/env python
-from setuptools import setup, find_packages
+from setuptools import setup
 import os, re
 
 PKG='oauth2'
@@ -31,7 +31,7 @@
       author="Joe Stump",
       author_email="joe@simplegeo.com",
       url="http://github.com/simplegeo/python-oauth2",
-      packages = find_packages(),
+      packages = ['oauth2'],
       install_requires = ['httplib2'],
       license = "MIT License",
       keywords="oauth",

From eb5e0f751d2baeb891fab87ea3ad9e53d7a00ff2 Mon Sep 17 00:00:00 2001
From: Joe Stump <joe@joestump.net>
Date: Tue, 28 Jul 2015 23:13:03 -0700
Subject: [PATCH 45/73] Make check for Authorization header case insensitive
 and also check for WSGI's HTTP_AUTHORIZATION in the headers.

---
 oauth2/__init__.py  | 11 ++++++---
 tests/test_oauth.py | 57 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 6c66de1f..36f5726c 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -519,10 +519,15 @@ def from_request(cls, http_method, http_url, headers=None, parameters=None,
             parameters = {}
  
         # Headers
-        if headers and 'Authorization' in headers:
-            auth_header = headers['Authorization']
+        if headers:
+            auth_header = None
+            for k, v in headers.items():
+                if k.lower() == 'authorization' or \
+                    k.upper() == 'HTTP_AUTHORIZATION':
+                    auth_header = v
+
             # Check that the authorization header is OAuth.
-            if auth_header[:6] == 'OAuth ':
+            if auth_header and auth_header[:6] == 'OAuth ':
                 auth_header = auth_header[6:]
                 try:
                     # Get the parameters from the header.
diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index 1399c291..bae3e514 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -914,6 +914,63 @@ def test_sign_request(self):
         req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), con, tok)
         self.assertEquals(req['oauth_signature'], 'IBw5mfvoCsDjgpcsVKbyvsDqQaU=')
 
+
+    def test_from_request_works_with_wsgi(self):
+        """Make sure WSGI header HTTP_AUTHORIZATION is detected correctly."""
+        url = "http://sp.example.com/"
+
+        params = {
+            'oauth_version': "1.0",
+            'oauth_nonce': "4572616e48616d6d65724c61686176",
+            'oauth_timestamp': "137131200",
+            'oauth_consumer_key': "0685bd9184jfhq22",
+            'oauth_signature_method': "HMAC-SHA1",
+            'oauth_token': "ad180jjd733klru7",
+            'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",
+        }
+
+        req = oauth.Request("GET", url, params)
+        headers = req.to_header()
+
+        # Munge the headers
+        headers['HTTP_AUTHORIZATION'] = headers['Authorization']
+        del headers['Authorization'] 
+
+        # Test from the headers
+        req = oauth.Request.from_request("GET", url, headers)
+        self.assertEquals(req.method, "GET")
+        self.assertEquals(req.url, url)
+        self.assertEquals(params, req.copy())
+
+
+    def test_from_request_is_case_insensitive_checking_for_auth(self):
+        """Checks for the Authorization header should be case insensitive."""
+        url = "http://sp.example.com/"
+
+        params = {
+            'oauth_version': "1.0",
+            'oauth_nonce': "4572616e48616d6d65724c61686176",
+            'oauth_timestamp': "137131200",
+            'oauth_consumer_key': "0685bd9184jfhq22",
+            'oauth_signature_method': "HMAC-SHA1",
+            'oauth_token': "ad180jjd733klru7",
+            'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",
+        }
+
+        req = oauth.Request("GET", url, params)
+        headers = req.to_header()
+
+        # Munge the headers
+        headers['authorization'] = headers['Authorization']
+        del headers['Authorization'] 
+
+        # Test from the headers
+        req = oauth.Request.from_request("GET", url, headers)
+        self.assertEquals(req.method, "GET")
+        self.assertEquals(req.url, url)
+        self.assertEquals(params, req.copy())
+
+
     def test_from_request(self):
         url = "http://sp.example.com/"
 

From c3d8b72e4a44d9285f4ea8cd81c8b9e20ef1783f Mon Sep 17 00:00:00 2001
From: "Daniel J Holmes (jaitaiwan)" <dan@centricwebestate.com>
Date: Thu, 30 Jul 2015 13:46:34 +1000
Subject: [PATCH 46/73] Should be compiling against 3

---
 .travis.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.travis.yml b/.travis.yml
index 7796bb8a..128885c6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,5 +2,7 @@ language: python
 python:
   - "2.6"
   - "2.7"
+  - "3.3"
+  - "3.4"
 install: "pip install -r requirements.txt"
 script: py.test 

From 007ac36ac8b1e7a9c5ab868f1b3fc1eb301c22a9 Mon Sep 17 00:00:00 2001
From: Rick Hanlon II <rickhanlonii@gmail.com>
Date: Fri, 31 Jul 2015 18:08:50 -0400
Subject: [PATCH 47/73] Fix p3k tests broken in merge

---
 oauth2/__init__.py  | 42 ++++++++++++++++++++++++------------------
 oauth2/_compat.py   |  4 ++++
 tests/test_oauth.py | 10 ++++------
 3 files changed, 32 insertions(+), 24 deletions(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 48d054a2..a1776a75 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -40,6 +40,8 @@
 from ._compat import unquote
 from ._compat import unquote_to_bytes
 from ._compat import urlencode
+from ._compat import urlsplit
+from ._compat import urlunsplit
 from ._compat import urlparse
 from ._compat import urlunparse
 from ._version import __version__
@@ -355,12 +357,11 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None,
         self.body = body
         self.is_form_encoded = is_form_encoded
 
-
     @setter
     def url(self, value):
         self.__dict__['url'] = value
         if value is not None:
-            scheme, netloc, path, params, query, fragment = urlparse(value)
+            scheme, netloc, path, query, fragment = urlsplit(value)
 
             # Exclude default port numbers.
             if scheme == 'http' and netloc[-3:] == ':80':
@@ -371,7 +372,7 @@ def url(self, value):
                 raise ValueError("Unsupported URL %s (%s)." % (value, scheme))
 
             # Normalized URL excludes params, query, and fragment.
-            self.normalized_url = urlunparse((scheme, netloc, path, None, None, None))
+            self.normalized_url = urlunsplit((scheme, netloc, path, None, None))
         else:
             self.normalized_url = None
             self.__dict__['url'] = None
@@ -416,21 +417,26 @@ def to_postdata(self):
     def to_url(self):
         """Serialize as a URL for a GET request."""
         base_url = urlparse(self.url)
-        try:
-            query = base_url.query
-        except AttributeError: #pragma NO COVER
-            # must be python <2.5
-            query = base_url[4]
-        query = parse_qs(to_utf8(query))
-        for k, v in self.items():
-            query.setdefault(to_utf8(k), []).append(to_utf8_optional_iterator(v))
-        
-        scheme = to_utf8(base_url.scheme)
-        netloc = to_utf8(base_url.netloc)
-        path = to_utf8(base_url.path)
-        params = to_utf8(base_url.params)
-        fragment = to_utf8(base_url.fragment)
-        
+
+        if PY3:
+            query = parse_qs(base_url.query)
+            for k, v in self.items():
+                query.setdefault(k, []).append(to_utf8_optional_iterator(v))
+            scheme = base_url.scheme
+            netloc = base_url.netloc
+            path = base_url.path
+            params = base_url.params
+            fragment = base_url.fragment
+        else:
+            query = parse_qs(to_utf8(base_url.query))
+            for k, v in self.items():
+                query.setdefault(to_utf8(k), []).append(to_utf8_optional_iterator(v))
+            scheme = to_utf8(base_url.scheme)
+            netloc = to_utf8(base_url.netloc)
+            path = to_utf8(base_url.path)
+            params = to_utf8(base_url.params)
+            fragment = to_utf8(base_url.fragment)
+
         url = (scheme, netloc, path, params, urlencode(query, True), fragment)
         return urlunparse(url)
 
diff --git a/oauth2/_compat.py b/oauth2/_compat.py
index d01905b5..b3f9ad01 100644
--- a/oauth2/_compat.py
+++ b/oauth2/_compat.py
@@ -31,6 +31,8 @@ def u(x, encoding='ascii'):
     from urllib.parse import unquote
     from urllib.parse import unquote_to_bytes
     from urllib.parse import urlencode
+    from urllib.parse import urlsplit
+    from urllib.parse import urlunsplit
     from urllib.parse import urlparse
     from urllib.parse import urlunparse
 else: #pragma NO COVER Python2
@@ -39,6 +41,8 @@ def u(x, encoding='ascii'):
     from urllib import quote
     from urllib import unquote
     from urllib import urlencode
+    from urlparse import urlsplit
+    from urlparse import urlunsplit
     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 fbae7d0d..a8b1e756 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -215,8 +215,7 @@ 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')
@@ -443,10 +442,10 @@ def test_to_url_nonascii(self):
         }
 
         req = oauth.Request("GET", url, params)
-        res = urlparse.urlparse(req.to_url())
+        res = urlparse(req.to_url())
 
         params['nonasciithing'] = params['nonasciithing'].encode('utf-8')
-        exp = urlparse.urlparse("%s?%s" % (url, urllib.urlencode(params)))
+        exp = urlparse("%s?%s" % (url, urlencode(params)))
 
         self.assertEquals(exp.netloc, res.netloc)
         self.assertEquals(exp.path, res.path)
@@ -1016,8 +1015,7 @@ def test_sign_request(self):
         params['oauth_token'] = tok.key
         params['oauth_consumer_key'] = con.key
         req = oauth.Request(method="GET", url=url, parameters=params)
-    
-        # TODO: this changes to b input?
+
         methods = {
             b'DX01TdHws7OninCLK9VztNTH1M4=': oauth.SignatureMethod_HMAC_SHA1(),
             b'con-test-secret&tok-test-secret': oauth.SignatureMethod_PLAINTEXT()

From 0104ab1fb77ac4d4336417e3a97483fa5bdc5c19 Mon Sep 17 00:00:00 2001
From: The Gitter Badger <badger@gitter.im>
Date: Sun, 2 Aug 2015 18:13:41 +0000
Subject: [PATCH 48/73] Added Gitter badge

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index 5855e208..c87a64c0 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
 
 # Overview
 
+[![Join the chat at https://gitter.im/joestump/python-oauth2](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/joestump/python-oauth2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
 This code was originally forked from [Leah Culver and Andy Smith's oauth.py code](http://github.com/leah/python-oauth/). Some of the tests come from a [fork by Vic Fryzel](http://github.com/shellsage/python-oauth), while a revamped Request class and more tests were merged in from [Mark Paschal's fork](http://github.com/markpasc/python-oauth). A number of notable differences exist between this code and its forefathers:
 
 * 100% unit test coverage.

From deb25b85ba315d756668ef9adeff16c61e47ee70 Mon Sep 17 00:00:00 2001
From: Daniel Joshua Holmes <dan@centricwebestate.com>
Date: Mon, 3 Aug 2015 09:26:48 +1000
Subject: [PATCH 49/73] Revert "Should be compiling against 3"

This reverts commit c3d8b72e4a44d9285f4ea8cd81c8b9e20ef1783f.
---
 .travis.yml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 128885c6..7796bb8a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,5 @@ language: python
 python:
   - "2.6"
   - "2.7"
-  - "3.3"
-  - "3.4"
 install: "pip install -r requirements.txt"
 script: py.test 

From d7c9e4c8324bc24beb1da1d5187b2fb1ee26d482 Mon Sep 17 00:00:00 2001
From: Daniel Joshua Holmes <dan@centricwebestate.com>
Date: Mon, 17 Aug 2015 12:20:19 +1000
Subject: [PATCH 50/73] Add classifiers to setup.py

Closes #150
---
 setup.py | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index 78119d69..ffb1a5b8 100755
--- a/setup.py
+++ b/setup.py
@@ -37,4 +37,12 @@
       keywords="oauth",
       zip_safe = True,
       test_suite="tests",
-      tests_require=['coverage', 'mock'])
+      tests_require=['coverage', 'mock'],
+      classifiers=[
+        'Programming Language :: Python :: 2.6',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 2 :: Only',
+        'License :: OSI Approved :: MIT License',
+        'Natural Language :: English',
+        'Development Status :: 5 - Production/Stable'
+      ])

From b15f785a1e0f51852f53a82aeb36c45b33b0d742 Mon Sep 17 00:00:00 2001
From: Daniel Holmes <daniel.holmes@djcentric.com>
Date: Wed, 19 Aug 2015 09:28:45 +1000
Subject: [PATCH 51/73] Revert "Add Classifiers to Setup.py"

---
 setup.py | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/setup.py b/setup.py
index ffb1a5b8..78119d69 100755
--- a/setup.py
+++ b/setup.py
@@ -37,12 +37,4 @@
       keywords="oauth",
       zip_safe = True,
       test_suite="tests",
-      tests_require=['coverage', 'mock'],
-      classifiers=[
-        'Programming Language :: Python :: 2.6',
-        'Programming Language :: Python :: 2.7',
-        'Programming Language :: Python :: 2 :: Only',
-        'License :: OSI Approved :: MIT License',
-        'Natural Language :: English',
-        'Development Status :: 5 - Production/Stable'
-      ])
+      tests_require=['coverage', 'mock'])

From 32f4997b05654860db006fd2638a03c601c92139 Mon Sep 17 00:00:00 2001
From: Rick Hanlon II <rickhanlonii@gmail.com>
Date: Tue, 8 Sep 2015 12:34:41 -0400
Subject: [PATCH 52/73] Fix p3k tests

---
 tests/test_oauth.py | 35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index a8b1e756..ae6e4d3d 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -23,6 +23,7 @@
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 THE SOFTWARE.
 """
+import sys
 import random
 import time
 import unittest
@@ -52,6 +53,7 @@
 _B2766 = b'\xe2\x9d\xa6' # u'\u2766' encoded to UTF-8
 _U2766 = u(_B2766, 'utf8') # u'\u2766'
 
+PY3 = sys.version_info >= (3,)
 
 class TestError(unittest.TestCase):
     def test_message(self):
@@ -473,12 +475,24 @@ def test_to_url_works_with_non_ascii_parameters(self):
         params.update(other_params)
 
         req = oauth.Request("GET", "http://example.com", params)
-        self.assertEquals(
-            req.to_url(), 
-            'http://example.com?oauth_consumer=asdfasdfasdf&'
-            'uni_unicode_2=%C3%A5%C3%85%C3%B8%C3%98&'
-            'uni_utf8=%C2%AE&multi=%5B%27FOO%27%2C+%27BAR%27%5D&'
-            'uni_unicode=%C2%AE&bar=foo&foo=baz')
+
+        # We need to split out the host and params and check individually since the order is not determinate.
+        url_parts = req.to_url().split("?")
+        host = url_parts[0]
+        params = dict(item.strip().split("=") for item in url_parts[1].split("&"))
+
+        expected_params = {
+            'uni_utf8': '%C2%AE',
+            'foo': 'baz',
+            'bar': 'foo',
+            'uni_unicode_2': '%C3%A5%C3%85%C3%B8%C3%98',
+            'uni_unicode': '%C2%AE',
+            'multi': '%5Bb%27FOO%27%2C+b%27BAR%27%5D',
+            'oauth_consumer': 'asdfasdfasdf'
+        }
+
+        self.assertEquals("http://example.com", host)
+        self.assertEquals(expected_params, params)
 
     def test_to_header(self):
         realm = "http://sp.example.com/"
@@ -902,10 +916,11 @@ def test_request_nonutf8_bytes(self, mock_make_nonce, mock_make_timestamp):
             'oauth_consumer_key': con.key
         }
 
-        # 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)
+        if not PY3:
+            # 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)
 
         # And if they pass an unicode, then we'll use it.
         url = u('http://sp.example.com/') + _U2019

From cdda5f510c0912253b0868cbe7672437d82733c4 Mon Sep 17 00:00:00 2001
From: Rick Hanlon II <rickhanlonii@gmail.com>
Date: Tue, 8 Sep 2015 13:19:56 -0400
Subject: [PATCH 53/73] Use correct test value.

---
 tests/test_oauth.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index ae6e4d3d..503387c9 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -465,7 +465,7 @@ def test_to_url_works_with_non_ascii_parameters(self):
         other_params = {
             u'foo': u'baz',
             u'bar': u'foo',
-            u'multi': [u'FOO',u'BAR'],
+            u'multi': [u'FOO', u'BAR'],
             u'uni_utf8': u'\xae',
             u'uni_unicode': u'\u00ae',
             u'uni_unicode_2': u'åÅøØ',
@@ -487,7 +487,7 @@ def test_to_url_works_with_non_ascii_parameters(self):
             'bar': 'foo',
             'uni_unicode_2': '%C3%A5%C3%85%C3%B8%C3%98',
             'uni_unicode': '%C2%AE',
-            'multi': '%5Bb%27FOO%27%2C+b%27BAR%27%5D',
+            'multi': '%5B%27FOO%27%2C+%27BAR%27%5D',
             'oauth_consumer': 'asdfasdfasdf'
         }
 

From ef42e4bf86ceb7f87b8249b6f5b0f9aaf967e223 Mon Sep 17 00:00:00 2001
From: "Daniel J Holmes (jaitaiwan)" <dan@centricwebestate.com>
Date: Sat, 12 Sep 2015 21:10:01 +1000
Subject: [PATCH 54/73] Update for py3k release

---
 .travis.yml         |   2 +
 README.md           | 360 +-------------------------------------------
 setup.py            |  23 ++-
 tests/test_oauth.py |   2 -
 4 files changed, 13 insertions(+), 374 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 7796bb8a..128885c6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,5 +2,7 @@ language: python
 python:
   - "2.6"
   - "2.7"
+  - "3.3"
+  - "3.4"
 install: "pip install -r requirements.txt"
 script: py.test 
diff --git a/README.md b/README.md
index 5855e208..ba61da2c 100644
--- a/README.md
+++ b/README.md
@@ -19,362 +19,6 @@ You can install `oauth2` via [the PIP package](https://pypi.python.org/pypi/oaut
     
 We recommend using [virtualenv](https://virtualenv.pypa.io/en/latest/).
 
-# Signing a Request
-
-```python
-import oauth2 as oauth
-import time
-
-# Set the API endpoint 
-url = "http://example.com/photos"
-
-# Set the base oauth_* parameters along with any other parameters required
-# for the API call.
-params = {
-    'oauth_version': "1.0",
-    'oauth_nonce': oauth.generate_nonce(),
-    'oauth_timestamp': int(time.time()),
-    'user': 'joestump',
-    'photoid': 555555555555
-}
-
-# Set up instances of our Token and Consumer. The Consumer.key and 
-# Consumer.secret are given to you by the API provider. The Token.key and
-# Token.secret is given to you after a three-legged authentication.
-token = oauth.Token(key="tok-test-key", secret="tok-test-secret")
-consumer = oauth.Consumer(key="con-test-key", secret="con-test-secret")
-
-# Set our token/key parameters
-params['oauth_token'] = token.key
-params['oauth_consumer_key'] = consumer.key
-
-# Create our request. Change method, etc. accordingly.
-req = oauth.Request(method="GET", url=url, parameters=params)
-
-# Sign the request.
-signature_method = oauth.SignatureMethod_HMAC_SHA1()
-req.sign_request(signature_method, consumer, token)
-```
-
-# Using the Client
-
-The <code>oauth2.Client</code> is based on <code>httplib2</code> and works just as you'd expect it to. The only difference is the first two arguments to the constructor are an instance of <code>oauth2.Consumer</code> and <code>oauth2.Token</code> (<code>oauth2.Token</code> is only needed for three-legged requests).
-
-```python
-import oauth2 as oauth
-
-# Create your consumer with the proper key/secret.
-consumer = oauth.Consumer(key="your-twitter-consumer-key", 
-    secret="your-twitter-consumer-secret")
-
-# Request token URL for Twitter.
-request_token_url = "https://api.twitter.com/oauth/request_token"
-
-# Create our client.
-client = oauth.Client(consumer)
-
-# The OAuth Client request works just like httplib2 for the most part.
-resp, content = client.request(request_token_url, "GET")
-print resp
-print content
-```
-
-# Twitter Three-legged OAuth Example
-
-Below is an example of how one would go through a three-legged OAuth flow to
-gain access to protected resources on Twitter. This is a simple CLI script, but
-can be easily translated to a web application.
-
-```python
-import urlparse
-import oauth2 as oauth
-
-consumer_key = 'my_key_from_twitter'
-consumer_secret = 'my_secret_from_twitter'
-
-request_token_url = 'https://api.twitter.com/oauth/request_token'
-access_token_url = 'https://api.twitter.com/oauth/access_token'
-authorize_url = 'https://api.twitter.com/oauth/authorize'
-
-consumer = oauth.Consumer(consumer_key, consumer_secret)
-client = oauth.Client(consumer)
-
-# Step 1: Get a request token. This is a temporary token that is used for 
-# having the user authorize an access token and to sign the request to obtain 
-# said access token.
-    
-resp, content = client.request(request_token_url, "GET")
-if resp['status'] != '200':
-    raise Exception("Invalid response %s." % resp['status'])
-
-request_token = dict(urlparse.parse_qsl(content))
-
-print "Request Token:"
-print "    - oauth_token        = %s" % request_token['oauth_token']
-print "    - oauth_token_secret = %s" % request_token['oauth_token_secret']
-print 
-
-# Step 2: Redirect to the provider. Since this is a CLI script we do not 
-# redirect. In a web application you would redirect the user to the URL
-# below.
-
-print "Go to the following link in your browser:"
-print "%s?oauth_token=%s" % (authorize_url, request_token['oauth_token'])
-print 
-
-# After the user has granted access to you, the consumer, the provider will
-# redirect you to whatever URL you have told them to redirect to. You can 
-# usually define this in the oauth_callback argument as well.
-accepted = 'n'
-while accepted.lower() == 'n':
-    accepted = raw_input('Have you authorized me? (y/n) ')
-oauth_verifier = raw_input('What is the PIN? ')
-
-# Step 3: Once the consumer has redirected the user back to the oauth_callback
-# URL you can request the access token the user has approved. You use the 
-# request token to sign this request. After this is done you throw away the
-# request token and use the access token returned. You should store this 
-# access token somewhere safe, like a database, for future use.
-token = oauth.Token(request_token['oauth_token'],
-    request_token['oauth_token_secret'])
-token.set_verifier(oauth_verifier)
-client = oauth.Client(consumer, token)
-
-resp, content = client.request(access_token_url, "POST")
-access_token = dict(urlparse.parse_qsl(content))
-
-print "Access Token:"
-print "    - oauth_token        = %s" % access_token['oauth_token']
-print "    - oauth_token_secret = %s" % access_token['oauth_token_secret']
-print
-print "You may now access protected resources using the access tokens above." 
-print
-```
-
-# Logging into Django w/ Twitter
-
-Twitter also has the ability to authenticate a user [via an OAuth flow](https://dev.twitter.com/docs/auth/sign-twitter). This
-flow is exactly like the three-legged OAuth flow, except you send them to a 
-slightly different URL to authorize them. 
-
-In this example we'll look at how you can implement this login flow using 
-Django and python-oauth2. 
-
-## Set up a Profile model
-
-You'll need a place to store all of your Twitter OAuth credentials after the
-user has logged in. In your app's `models.py` file you should add something
-that resembles the following model.
-
-```python
-class Profile(models.Model):
-    user = models.ForeignKey(User)
-    oauth_token = models.CharField(max_length=200)
-    oauth_secret = models.CharField(max_length=200)
-```
-
-## Set up your Django views
-
-### `urls.py`
-
-Your `urls.py` should look something like the following. Basically, you need to
-have a login URL, a callback URL that Twitter will redirect your users back to,
-and a logout URL.
-
-In this example `^login/` and `twitter_login` will send the user to Twitter to
-be logged in, `^login/authenticated/` and `twitter_authenticated` will confirm
-the login, create the account if necessary, and log the user into the 
-application, and `^logout`/ logs the user out in the `twitter_logout` view.
-
-```python
-from django.conf.urls.defaults import *
-from django.contrib import admin
-from mytwitterapp.views import twitter_login, twitter_logout, \
-    twitter_authenticated
-
-admin.autodiscover()
-
-urlpatterns = patterns('',
-    url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
-    url(r'^admin/', include(admin.site.urls)),
-    url(r'^login/?$', twitter_login),
-    url(r'^logout/?$', twitter_logout),
-    url(r'^login/authenticated/?$', twitter_authenticated),
-)
-```
-
-### `views.py`
-
-*NOTE:* The following code was coded for Python 2.4 so some of the libraries 
-and code here might need to be updated if you are using Python 2.6+. 
-
-```python
-# Python
-import oauth2 as oauth
-import cgi
-
-# Django
-from django.shortcuts import render_to_response
-from django.http import HttpResponseRedirect
-from django.conf import settings
-from django.contrib.auth import authenticate, login, logout
-from django.contrib.auth.models import User
-from django.contrib.auth.decorators import login_required
-
-# Project
-from mytwitterapp.models import Profile
-
-# It's probably a good idea to put your consumer's OAuth token and
-# OAuth secret into your project's settings. 
-consumer = oauth.Consumer(settings.TWITTER_TOKEN, settings.TWITTER_SECRET)
-client = oauth.Client(consumer)
-
-request_token_url = 'https://api.twitter.com/oauth/request_token'
-access_token_url = 'https://api.twitter.com/oauth/access_token'
-
-# This is the slightly different URL used to authenticate/authorize.
-authenticate_url = 'https://api.twitter.com/oauth/authenticate'
-
-def twitter_login(request):
-    # Step 1. Get a request token from Twitter.
-    resp, content = client.request(request_token_url, "GET")
-    if resp['status'] != '200':
-        raise Exception("Invalid response from Twitter.")
-
-    # Step 2. Store the request token in a session for later use.
-    request.session['request_token'] = dict(cgi.parse_qsl(content))
-
-    # Step 3. Redirect the user to the authentication URL.
-    url = "%s?oauth_token=%s" % (authenticate_url,
-        request.session['request_token']['oauth_token'])
-
-    return HttpResponseRedirect(url)
-
-    
-@login_required
-def twitter_logout(request):
-    # Log a user out using Django's logout function and redirect them
-    # back to the homepage.
-    logout(request)
-    return HttpResponseRedirect('/')
-
-def twitter_authenticated(request):
-    # Step 1. Use the request token in the session to build a new client.
-    token = oauth.Token(request.session['request_token']['oauth_token'],
-        request.session['request_token']['oauth_token_secret'])
-    token.set_verifier(request.GET['oauth_verifier'])
-    client = oauth.Client(consumer, token)
-
-    # Step 2. Request the authorized access token from Twitter.
-    resp, content = client.request(access_token_url, "GET")
-    if resp['status'] != '200':
-        print content
-        raise Exception("Invalid response from Twitter.")
-
-    """
-    This is what you'll get back from Twitter. Note that it includes the
-    user's user_id and screen_name.
-    {
-        'oauth_token_secret': 'IcJXPiJh8be3BjDWW50uCY31chyhsMHEhqJVsphC3M',
-        'user_id': '120889797', 
-        'oauth_token': '120889797-H5zNnM3qE0iFoTTpNEHIz3noL9FKzXiOxwtnyVOD',
-        'screen_name': 'heyismysiteup'
-    }
-    """
-    access_token = dict(cgi.parse_qsl(content))
-
-    # Step 3. Lookup the user or create them if they don't exist.
-    try:
-        user = User.objects.get(username=access_token['screen_name'])
-    except User.DoesNotExist:
-        # When creating the user I just use their screen_name@twitter.com
-        # for their email and the oauth_token_secret for their password.
-        # These two things will likely never be used. Alternatively, you 
-        # can prompt them for their email here. Either way, the password 
-        # should never be used.
-        user = User.objects.create_user(access_token['screen_name'],
-            '%s@twitter.com' % access_token['screen_name'],
-            access_token['oauth_token_secret'])
-
-        # Save our permanent token and secret for later.
-        profile = Profile()
-        profile.user = user
-        profile.oauth_token = access_token['oauth_token']
-        profile.oauth_secret = access_token['oauth_token_secret']
-        profile.save()
-    
-    # Authenticate the user and log them in using Django's pre-built 
-    # functions for these things.
-    user = authenticate(username=access_token['screen_name'],
-        password=access_token['oauth_token_secret'])
-    login(request, user)
-
-    return HttpResponseRedirect('/')
-```
-
-### `settings.py`
-
-* You'll likely want to set `LOGIN_URL` to `/login/` so that users are properly redirected to your Twitter login handler when you use `@login_required` in other parts of your Django app.
-* You can also set `AUTH_PROFILE_MODULE = 'mytwitterapp.Profile'` so that you can easily access the Twitter OAuth token/secret for that user using the `User.get_profile()` method in Django.
-
-# XOAUTH for IMAP and SMTP
-
-Gmail supports OAuth over IMAP and SMTP via a standard they call XOAUTH. This allows you to authenticate against Gmail's IMAP and SMTP servers using an OAuth token and secret. It also has the added benefit of allowing you to use vanilla SMTP and IMAP libraries. The `python-oauth2` package provides both IMAP and SMTP libraries that implement XOAUTH and wrap `imaplib.IMAP4_SSL` and `smtplib.SMTP`. This allows you to connect to Gmail with OAuth credentials using standard Python libraries. 
-
-## IMAP
-
-```python
-import oauth2 as oauth
-import oauth2.clients.imap as imaplib
-
-# Set up your Consumer and Token as per usual. Just like any other
-# three-legged OAuth request.
-consumer = oauth.Consumer('your_consumer_key', 'your_consumer_secret')
-token = oauth.Token('your_users_3_legged_token', 
-    'your_users_3_legged_token_secret')
-
-# Setup the URL according to Google's XOAUTH implementation. Be sure
-# to replace the email here with the appropriate email address that
-# you wish to access.
-url = "https://mail.google.com/mail/b/your_users_email@gmail.com/imap/"
-
-conn = imaplib.IMAP4_SSL('imap.googlemail.com')
-conn.debug = 4 
-
-# This is the only thing in the API for impaplib.IMAP4_SSL that has 
-# changed. You now authenticate with the URL, consumer, and token.
-conn.authenticate(url, consumer, token)
-
-# Once authenticated everything from the impalib.IMAP4_SSL class will 
-# work as per usual without any modification to your code.
-conn.select('INBOX')
-print conn.list()
-```
-
-## SMTP
-
-```python
-import oauth2 as oauth
-import oauth2.clients.smtp as smtplib
-
-# Set up your Consumer and Token as per usual. Just like any other
-# three-legged OAuth request.
-consumer = oauth.Consumer('your_consumer_key', 'your_consumer_secret')
-token = oauth.Token('your_users_3_legged_token', 
-    'your_users_3_legged_token_secret')
-
-# Setup the URL according to Google's XOAUTH implementation. Be sure
-# to replace the email here with the appropriate email address that
-# you wish to access.
-url = "https://mail.google.com/mail/b/your_users_email@gmail.com/smtp/"
-
-conn = smtplib.SMTP('smtp.googlemail.com', 587)
-conn.set_debuglevel(True)
-conn.ehlo('test')
-conn.starttls()
-
-# Again the only thing modified from smtplib.SMTP is the authenticate
-# method, which works identically to the imaplib.IMAP4_SSL method.
-conn.authenticate(url, consumer, token)
-```
+# Examples
 
+Examples can be found in the [wiki](./wiki/)
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 140bc103..8e27f3fc 100755
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,6 @@
 #!/usr/bin/env python
-from setuptools import setup
+from __future__ import print_function
+from setuptools import setup, find_packages
 import os, re
 
 PKG='oauth2'
@@ -15,7 +16,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)
@@ -30,17 +31,19 @@
       description="library for OAuth version 1.0",
       author="Joe Stump",
       author_email="joe@simplegeo.com",
-      url="http://github.com/simplegeo/python-oauth2",
-      packages = ['oauth2'],
+      url="http://github.com/joestump/python-oauth2",
       classifiers=[
         "Intended Audience :: Developers",
         "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 :: 3.4",
         "Programming Language :: Python :: Implementation :: CPython",
+        "Development Status :: 5 - Production/Stable",
+        "Natural Language :: English",
+        "License :: OSI Approved :: MIT License"
       ],
       packages = find_packages(),
       install_requires = ['httplib2'],
@@ -48,12 +51,4 @@
       keywords="oauth",
       zip_safe = True,
       test_suite="tests",
-      tests_require=['coverage', 'mock'],
-      classifiers=[
-        'Programming Language :: Python :: 2.6',
-        'Programming Language :: Python :: 2.7',
-        'Programming Language :: Python :: 2 :: Only',
-        'License :: OSI Approved :: MIT License',
-        'Natural Language :: English',
-        'Development Status :: 5 - Production/Stable'
-      ])
+      tests_require=['coverage', 'mock'])
diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index 503387c9..58854564 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -465,7 +465,6 @@ def test_to_url_works_with_non_ascii_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'åÅøØ',
@@ -487,7 +486,6 @@ def test_to_url_works_with_non_ascii_parameters(self):
             'bar': 'foo',
             'uni_unicode_2': '%C3%A5%C3%85%C3%B8%C3%98',
             'uni_unicode': '%C2%AE',
-            'multi': '%5B%27FOO%27%2C+%27BAR%27%5D',
             'oauth_consumer': 'asdfasdfasdf'
         }
 

From 0fddbbe84b911e2fc8f24191f1e42dbd5f60acf8 Mon Sep 17 00:00:00 2001
From: "Daniel J Holmes (jaitaiwan)" <dan@centricwebestate.com>
Date: Sat, 12 Sep 2015 21:15:01 +1000
Subject: [PATCH 55/73] Minor copy edits

---
 README.md | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/README.md b/README.md
index ba61da2c..f51bb8c1 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,7 @@
 [![Build Status](https://travis-ci.org/joestump/python-oauth2.svg)](https://travis-ci.org/joestump/python-oauth2)
 
 # Overview
-
-This code was originally forked from [Leah Culver and Andy Smith's oauth.py code](http://github.com/leah/python-oauth/). Some of the tests come from a [fork by Vic Fryzel](http://github.com/shellsage/python-oauth), while a revamped Request class and more tests were merged in from [Mark Paschal's fork](http://github.com/markpasc/python-oauth). A number of notable differences exist between this code and its forefathers:
-
-* 100% unit test coverage.
-* The <code>DataStore</code> object has been completely ripped out. While creating unit tests for the library I found several substantial bugs with the implementation and confirmed with Andy Smith that it was never fully baked.
-* Classes are no longer prefixed with <code>OAuth</code>.
-* The <code>Request</code> class now extends from <code>dict</code>.
-* The library is likely no longer compatible with Python 2.3.
-* The <code>Client</code> class works and extends from <code>httplib2</code>. It's a thin wrapper that handles automatically signing any normal HTTP request you might wish to make.
+python-oauth2 is a python oauth library fully compatible with python versions: 2.6, 2.7, 3.3 and 3.4. This library is depended on by many other downstream packages such as Flask-Oauth.
 
 # Installing
 
@@ -21,4 +13,22 @@ We recommend using [virtualenv](https://virtualenv.pypa.io/en/latest/).
 
 # Examples
 
-Examples can be found in the [wiki](./wiki/)
\ No newline at end of file
+Examples can be found in the [wiki](./wiki/)
+
+# Running tests
+You can run tests using the following at the command line:
+
+    $ pip install -r requirements.txt
+    $ python setup.py test
+
+
+# History
+
+This code was originally forked from [Leah Culver and Andy Smith's oauth.py code](http://github.com/leah/python-oauth/). Some of the tests come from a [fork by Vic Fryzel](http://github.com/shellsage/python-oauth), while a revamped Request class and more tests were merged in from [Mark Paschal's fork](http://github.com/markpasc/python-oauth). A number of notable differences exist between this code and its forefathers:
+
+* 100% unit test coverage.
+* The <code>DataStore</code> object has been completely ripped out. While creating unit tests for the library I found several substantial bugs with the implementation and confirmed with Andy Smith that it was never fully baked.
+* Classes are no longer prefixed with <code>OAuth</code>.
+* The <code>Request</code> class now extends from <code>dict</code>.
+* The library is likely no longer compatible with Python 2.3.
+* The <code>Client</code> class works and extends from <code>httplib2</code>. It's a thin wrapper that handles automatically signing any normal HTTP request you might wish to make.
\ No newline at end of file

From acdd693ce893aab97437c5c29284d1a2be914f4c Mon Sep 17 00:00:00 2001
From: "Daniel J Holmes (jaitaiwan)" <dan@centricwebestate.com>
Date: Sat, 12 Sep 2015 21:30:23 +1000
Subject: [PATCH 56/73] Bump Version

---
 oauth2/_version.py | 4 ++--
 setup.cfg          | 2 ++
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/oauth2/_version.py b/oauth2/_version.py
index a0e9e36d..0084e61f 100644
--- a/oauth2/_version.py
+++ b/oauth2/_version.py
@@ -1,10 +1,10 @@
 # This is the version of this source code.
 
-manual_verstr = "1.5"
+manual_verstr = "1.9"
 
 
 
-auto_build_num = "211"
+auto_build_num = "0"
 
 
 
diff --git a/setup.cfg b/setup.cfg
index 1266c15f..f0dddbe6 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -3,3 +3,5 @@ nocapture=1
 cover-package=oauth2
 cover-erase=1
 
+[bdist_wheel]
+universal=1
\ No newline at end of file

From 001ad062d85cdbcaadb66754624b8e34ecb3532f Mon Sep 17 00:00:00 2001
From: "Daniel J Holmes (jaitaiwan)" <dan@centricwebestate.com>
Date: Sat, 12 Sep 2015 21:58:16 +1000
Subject: [PATCH 57/73] Add helpful images

---
 README.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index a6c8eb39..d63a483e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
-[![Join the chat at https://gitter.im/joestump/python-oauth2](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/joestump/python-oauth2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+[![Join the chat at https://gitter.im/joestump/python-oauth2](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/joestump/python-oauth2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)[![Build Status](http://img.shields.io/travis-ci/joestump/python-oauth2.png?branch=master&style=flat-square)](https://travis-ci.org/joestump/python-oauth2)
+
+![Number of issues](https://img.shields.io/github/issues/joestump/python-oauth2.svg)![Licence MIT](https://img.shields.io/badge/license-MIT-blue.svg)
 
 # Overview
 python-oauth2 is a python oauth library fully compatible with python versions: 2.6, 2.7, 3.3 and 3.4. This library is depended on by many other downstream packages such as Flask-Oauth.

From 9d5a569fc9edda678102edccb330e1f692122a5a Mon Sep 17 00:00:00 2001
From: "Daniel J Holmes (jaitaiwan)" <dan@centricwebestate.com>
Date: Sat, 12 Sep 2015 22:43:08 +1000
Subject: [PATCH 58/73] Bump Version

I screwed up the pypi release
---
 oauth2/_version.py | 2 +-
 setup.py           | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/oauth2/_version.py b/oauth2/_version.py
index 0084e61f..3b813cb2 100644
--- a/oauth2/_version.py
+++ b/oauth2/_version.py
@@ -4,7 +4,7 @@
 
 
 
-auto_build_num = "0"
+auto_build_num = "0.post1"
 
 
 
diff --git a/setup.py b/setup.py
index 8e27f3fc..eba6e2ef 100755
--- a/setup.py
+++ b/setup.py
@@ -28,7 +28,7 @@
 
 setup(name=PKG,
       version=verstr,
-      description="library for OAuth version 1.0",
+      description="library for OAuth version 1.9",
       author="Joe Stump",
       author_email="joe@simplegeo.com",
       url="http://github.com/joestump/python-oauth2",

From 1b46e90361ec3c1ccf970af459c18d0327f38650 Mon Sep 17 00:00:00 2001
From: Alex Chan <alex@alexwlchan.net>
Date: Sat, 3 Oct 2015 12:20:01 +0100
Subject: [PATCH 59/73] Fix broken Wiki link in README

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 6f4ce33d..b12a9904 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ We recommend using [virtualenv](https://virtualenv.pypa.io/en/latest/).
 
 # Examples
 
-Examples can be found in the [wiki](./wiki/)
+Examples can be found in the [wiki](https://github.com/joestump/python-oauth2/wiki)
 
 # Running tests
 You can run tests using the following at the command line:
@@ -31,4 +31,4 @@ This code was originally forked from [Leah Culver and Andy Smith's oauth.py code
 * Classes are no longer prefixed with <code>OAuth</code>.
 * The <code>Request</code> class now extends from <code>dict</code>.
 * The library is likely no longer compatible with Python 2.3.
-* The <code>Client</code> class works and extends from <code>httplib2</code>. It's a thin wrapper that handles automatically signing any normal HTTP request you might wish to make.
\ No newline at end of file
+* The <code>Client</code> class works and extends from <code>httplib2</code>. It's a thin wrapper that handles automatically signing any normal HTTP request you might wish to make.

From 604f8e0cb41c969a273aa85b02ea7857c5406e55 Mon Sep 17 00:00:00 2001
From: Steve Peak <steve@stevepeak.net>
Date: Wed, 7 Oct 2015 11:33:51 +0200
Subject: [PATCH 60/73] Submit coverage reports to Codecov

---
 .travis.yml | 9 +++++++--
 README.md   | 2 +-
 2 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 128885c6..a20f7099 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,5 +4,10 @@ python:
   - "2.7"
   - "3.3"
   - "3.4"
-install: "pip install -r requirements.txt"
-script: py.test 
+install:
+  - pip install -r requirements.txt
+  - pip install codecov pytest-cov
+script:
+  - py.test --cov=oauth2
+after_success:
+  - codecov
diff --git a/README.md b/README.md
index b12a9904..39198d2a 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[![Join the chat at https://gitter.im/joestump/python-oauth2](https://img.shields.io/badge/gitter-join%20chat-1dce73.svg?style=flat-square)](https://gitter.im/joestump/python-oauth2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](http://img.shields.io/travis-ci/joestump/python-oauth2.png?branch=master&style=flat-square)](https://travis-ci.org/joestump/python-oauth2) ![Number of issues](https://img.shields.io/github/issues/joestump/python-oauth2.svg?style=flat-square) ![Licence MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)
+[![Join the chat at https://gitter.im/joestump/python-oauth2](https://img.shields.io/badge/gitter-join%20chat-1dce73.svg?style=flat-square)](https://gitter.im/joestump/python-oauth2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](http://img.shields.io/travis-ci/joestump/python-oauth2.png?branch=master&style=flat-square)](https://travis-ci.org/joestump/python-oauth2) [![Coverage](https://img.shields.io/codecov/c/github/joestump/python-oauth2.svg?style=flat-square)](https://codecov.io/gh/joestump/python-oauth2) ![Number of issues](https://img.shields.io/github/issues/joestump/python-oauth2.svg?style=flat-square) ![Licence MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)
 
 # Overview
 python-oauth2 is a python oauth library fully compatible with python versions: 2.6, 2.7, 3.3 and 3.4. This library is depended on by many other downstream packages such as Flask-Oauth.

From 87ac20b353cdc0f5816f10b33b537fca5ea1993c Mon Sep 17 00:00:00 2001
From: Tom Leach <tom@leach.it>
Date: Wed, 18 Nov 2015 11:07:12 -0500
Subject: [PATCH 61/73] Make more explicit about lack of OAuth 2.0 support

Three separate projects at my company have wasted time attempting to use this library to build an OAuth 2.0 client, simply because it's named "oauth2". Indeed there is an entire [thread](https://github.com/joestump/python-oauth2/issues/83) about this.

This banner makes it clear to anyone evaluating the library what it does and does not support.
---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index 39198d2a..67ad4222 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
 [![Join the chat at https://gitter.im/joestump/python-oauth2](https://img.shields.io/badge/gitter-join%20chat-1dce73.svg?style=flat-square)](https://gitter.im/joestump/python-oauth2?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](http://img.shields.io/travis-ci/joestump/python-oauth2.png?branch=master&style=flat-square)](https://travis-ci.org/joestump/python-oauth2) [![Coverage](https://img.shields.io/codecov/c/github/joestump/python-oauth2.svg?style=flat-square)](https://codecov.io/gh/joestump/python-oauth2) ![Number of issues](https://img.shields.io/github/issues/joestump/python-oauth2.svg?style=flat-square) ![Licence MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)
 
+## Note: This library implements OAuth 1.0 and *not OAuth 2.0*. 
+
 # Overview
 python-oauth2 is a python oauth library fully compatible with python versions: 2.6, 2.7, 3.3 and 3.4. This library is depended on by many other downstream packages such as Flask-Oauth.
 

From 9c77664a0735a276a1c7eab7d404ae256f8197fa Mon Sep 17 00:00:00 2001
From: Louis Sautier <sautier.louis@gmail.com>
Date: Wed, 18 Jan 2017 10:42:13 +0100
Subject: [PATCH 62/73] Do not install the "tests" package

---
 setup.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index eba6e2ef..6efa3914 100755
--- a/setup.py
+++ b/setup.py
@@ -45,7 +45,7 @@
         "Natural Language :: English",
         "License :: OSI Approved :: MIT License"
       ],
-      packages = find_packages(),
+      packages = find_packages(exclude=['tests']),
       install_requires = ['httplib2'],
       license = "MIT License",
       keywords="oauth",

From 99d557eba21f935c08180670d8f322554929a69a Mon Sep 17 00:00:00 2001
From: Louis Sautier <sautier.louis@gmail.com>
Date: Wed, 18 Jan 2017 10:46:22 +0100
Subject: [PATCH 63/73] Remove coverage from tests requirements

---
 setup.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setup.py b/setup.py
index eba6e2ef..95b66e08 100755
--- a/setup.py
+++ b/setup.py
@@ -51,4 +51,4 @@
       keywords="oauth",
       zip_safe = True,
       test_suite="tests",
-      tests_require=['coverage', 'mock'])
+      tests_require=['mock'])

From 95dbff6d7ae0ab2b0a92204cf1e10dce7f403cba Mon Sep 17 00:00:00 2001
From: Sylvain <sylvain.desodt+github@gmail.com>
Date: Wed, 8 Mar 2017 15:26:57 +0100
Subject: [PATCH 64/73] Add most recent Python versions in Travis CI

Add more recent Python versions including development branches and nightly build.
---
 .travis.yml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/.travis.yml b/.travis.yml
index a20f7099..32191e78 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,11 @@ python:
   - "2.7"
   - "3.3"
   - "3.4"
+  - "3.5"
+  - "3.6"
+  - "3.6-dev"
+  - "3.7-dev"
+  - "nightly"
 install:
   - pip install -r requirements.txt
   - pip install codecov pytest-cov

From 7dd5d758120d64dd7ad8a0b1af10e6283a7320aa Mon Sep 17 00:00:00 2001
From: Nik Nyby <nnyby@columbia.edu>
Date: Mon, 23 Oct 2017 13:45:27 -0400
Subject: [PATCH 65/73] Remove python 3.3, add python 3.5 and 3.6

Python 3.3 has reached end of life: https://www.python.org/downloads/release/python-336/
---
 .travis.yml | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index a20f7099..ad53945a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,8 +2,9 @@ language: python
 python:
   - "2.6"
   - "2.7"
-  - "3.3"
   - "3.4"
+  - "3.5"
+  - "3.6"
 install:
   - pip install -r requirements.txt
   - pip install codecov pytest-cov

From 3e388933417dcfa6fa1e8487b2014be9c900e754 Mon Sep 17 00:00:00 2001
From: martyni <martynjamespratt@gmail.com>
Date: Fri, 10 Nov 2017 12:50:32 +0000
Subject: [PATCH 66/73] bug fix

---
 oauth2/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index a1776a75..cc64ae80 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -490,7 +490,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(sha1(self.body).digest())
+            self['oauth_body_hash'] = base64.b64encode(sha1(u'{}'.format(self.body)).digest())
 
         if 'oauth_consumer_key' not in self:
             self['oauth_consumer_key'] = consumer.key

From 7de768862e19185aca941784c16f9abf171e2c20 Mon Sep 17 00:00:00 2001
From: martyni <martynjamespratt@gmail.com>
Date: Fri, 10 Nov 2017 13:00:05 +0000
Subject: [PATCH 67/73] removed unicode str

---
 oauth2/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index cc64ae80..73817f36 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -490,7 +490,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(sha1(u'{}'.format(self.body)).digest())
+            self['oauth_body_hash'] = base64.b64encode(sha1('{}'.format(self.body)).digest())
 
         if 'oauth_consumer_key' not in self:
             self['oauth_consumer_key'] = consumer.key

From 0295f507df9c2de07e2b985987d70fefcf1c51f2 Mon Sep 17 00:00:00 2001
From: martyni <martynjamespratt@gmail.com>
Date: Fri, 10 Nov 2017 13:05:13 +0000
Subject: [PATCH 68/73] bug fix

---
 oauth2/__init__.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 73817f36..3849b06f 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -490,7 +490,9 @@ 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(sha1('{}'.format(self.body)).digest())
+            if not self.body:
+               self.body = ''
+            self['oauth_body_hash'] = base64.b64encode(sha1(self.body).digest())
 
         if 'oauth_consumer_key' not in self:
             self['oauth_consumer_key'] = consumer.key

From 46bbc5ea27da68e776b5e2f1aa3b8d6c6905653f Mon Sep 17 00:00:00 2001
From: martyni <martynjamespratt@gmail.com>
Date: Fri, 10 Nov 2017 13:11:11 +0000
Subject: [PATCH 69/73] testing

---
 oauth2/__init__.py | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 3849b06f..1b56ee14 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -489,9 +489,7 @@ def sign_request(self, signature_method, consumer, token):
             # 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."
-            if not self.body:
-               self.body = ''
+            # request bodies. "
             self['oauth_body_hash'] = base64.b64encode(sha1(self.body).digest())
 
         if 'oauth_consumer_key' not in self:

From d4b0b6d6d12a2fe519a12e6fe9b80ac2fef6b9c8 Mon Sep 17 00:00:00 2001
From: martyni <martynjamespratt@gmail.com>
Date: Fri, 10 Nov 2017 13:21:50 +0000
Subject: [PATCH 70/73] encoded to utf-8

---
 oauth2/__init__.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index 1b56ee14..0e02e62e 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -489,7 +489,9 @@ def sign_request(self, signature_method, consumer, token):
             # 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. "
+            # request bodies."
+            if not self.body:
+               self.body = ''.encode('utf-8')
             self['oauth_body_hash'] = base64.b64encode(sha1(self.body).digest())
 
         if 'oauth_consumer_key' not in self:

From bc8b38024ba723acb6f89ce32658d28ea9a61c34 Mon Sep 17 00:00:00 2001
From: Tim Sheerman-Chase <tim2009@sheerman-chase.org.uk>
Date: Mon, 4 Dec 2017 04:24:22 +0000
Subject: [PATCH 71/73] Test for issue #207

---
 tests/test_oauth.py | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/tests/test_oauth.py b/tests/test_oauth.py
index 58854564..b9802cbc 100644
--- a/tests/test_oauth.py
+++ b/tests/test_oauth.py
@@ -1210,7 +1210,7 @@ def sign(self, request, consumer, token):
 
 class TestServer(unittest.TestCase):
     def setUp(self):
-        url = "http://sp.example.com/"
+        self.url = "http://sp.example.com/"
 
         params = {
             'oauth_version': "1.0",
@@ -1227,7 +1227,7 @@ def setUp(self):
 
         params['oauth_token'] = self.token.key
         params['oauth_consumer_key'] = self.consumer.key
-        self.request = oauth.Request(method="GET", url=url, parameters=params)
+        self.request = oauth.Request(method="GET", url=self.url, parameters=params)
 
         signature_method = oauth.SignatureMethod_HMAC_SHA1()
         self.request.sign_request(signature_method, self.consumer, self.token)
@@ -1269,6 +1269,18 @@ def test_verify_request(self):
         self.assertEqual(parameters['foo'], 59)
         self.assertEqual(parameters['multi'], ['FOO','BAR'])
 
+    def test_verify_request_query_string(self):
+        server = oauth.Server()
+        server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1())
+
+        signature_method = oauth.SignatureMethod_HMAC_SHA1()
+        request2 = oauth.Request.from_request("GET", self.url, query_string=urlencode(dict(self.request)))
+        request2.sign_request(signature_method, self.consumer, self.token)
+        request3 = oauth.Request.from_request("GET", self.url, query_string=urlencode(dict(request2)))        
+
+        parameters = server.verify_request(request3, self.consumer,
+            self.token)
+
     def test_verify_request_missing_signature(self):
         from oauth2 import MissingSignature
         server = oauth.Server()

From 281efc499d188b26e05f913ff8c0c2089e6a39bf Mon Sep 17 00:00:00 2001
From: Tim Sheerman-Chase <tim2009@sheerman-chase.org.uk>
Date: Mon, 4 Dec 2017 05:03:24 +0000
Subject: [PATCH 72/73] Hacky fix for #207

---
 oauth2/__init__.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index a1776a75..e588199e 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -353,6 +353,7 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None,
             for k, v in parameters.items():
                 k = to_unicode(k)
                 v = to_unicode_optional_iterator(v)
+
                 self[k] = v
         self.body = body
         self.is_form_encoded = is_form_encoded
@@ -540,6 +541,7 @@ def from_request(cls, http_method, http_url, headers=None, parameters=None,
         # GET or POST query string.
         if query_string:
             query_params = cls._split_url_string(query_string)
+
             parameters.update(query_params)
  
         # URL parameters.
@@ -757,6 +759,8 @@ def _check_signature(self, request, consumer, token):
         signature = request.get('oauth_signature')
         if signature is None:
             raise MissingSignature('Missing oauth_signature.')
+        if isinstance(signature, str):
+            signature = signature.encode('ascii', 'ignore')
 
         # Validate the signature.
         valid = signature_method.check(request, consumer, token, signature)

From 631e8a517a924081efd433976d8b413f932f4cc0 Mon Sep 17 00:00:00 2001
From: Igor Cheledinov <cheledinov@gmail.com>
Date: Tue, 23 Jan 2018 00:08:23 +0300
Subject: [PATCH 73/73] fix problem with python3.6

---
 oauth2/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/oauth2/__init__.py b/oauth2/__init__.py
index a1776a75..bbc8763b 100644
--- a/oauth2/__init__.py
+++ b/oauth2/__init__.py
@@ -490,7 +490,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(sha1(self.body).digest())
+            self['oauth_body_hash'] = base64.b64encode(sha1(to_utf8(self.body)).digest())
 
         if 'oauth_consumer_key' not in self:
             self['oauth_consumer_key'] = consumer.key
