Skip to content

Commit 0521544

Browse files
committed
Initial Py3k compatibility.
1 parent a83f4a2 commit 0521544

File tree

3 files changed

+164
-80
lines changed

3 files changed

+164
-80
lines changed

oauth2/__init__.py

Lines changed: 83 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,34 @@
2626
import urllib
2727
import time
2828
import random
29-
import urlparse
29+
try:
30+
import urllib.parse as urlparse
31+
from urllib.parse import quote
32+
from urllib.parse import splithost
33+
from urllib.parse import splittype
34+
from urllib.parse import unquote
35+
from urllib.parse import urlencode
36+
except ImportError:
37+
import urlparse
38+
from urllib import quote
39+
from urllib import splithost
40+
from urllib import splittype
41+
from urllib import unquote
42+
from urllib import urlencode
3043
import hmac
3144
import binascii
3245
import httplib2
46+
import sys
3347

3448
try:
35-
from urlparse import parse_qs
36-
parse_qs # placate pyflakes
49+
from urllib.parse import parse_qs
3750
except ImportError:
38-
# fall back for Python 2.5
39-
from cgi import parse_qs
51+
try:
52+
from urlparse import parse_qs
53+
parse_qs # placate pyflakes
54+
except ImportError:
55+
# fall back for Python 2.5
56+
from cgi import parse_qs
4057

4158
try:
4259
from hashlib import sha1
@@ -52,6 +69,35 @@
5269
OAUTH_VERSION = '1.0' # Hi Blaine!
5370
HTTP_METHOD = 'GET'
5471
SIGNATURE_METHOD = 'PLAINTEXT'
72+
PY3 = sys.version > '3'
73+
74+
75+
try:
76+
basestring
77+
except NameError:
78+
basestring = str
79+
try:
80+
unicode
81+
except NameError:
82+
unicode = str
83+
84+
85+
def b(string):
86+
if PY3:
87+
return string.encode('ascii')
88+
return string
89+
90+
91+
def s(bytes):
92+
if PY3:
93+
return bytes.decode('ascii')
94+
return bytes
95+
96+
97+
def iteritems(d):
98+
if PY3:
99+
return d.items()
100+
return d.iteritems()
55101

56102

57103
class Error(RuntimeError):
@@ -87,7 +133,7 @@ def build_xoauth_string(url, consumer, token=None):
87133
request.sign_request(signing_method, consumer, token)
88134

89135
params = []
90-
for k, v in sorted(request.iteritems()):
136+
for k, v in sorted(iteritems(request)):
91137
if v is not None:
92138
params.append('%s="%s"' % (k, escape(v)))
93139

@@ -102,7 +148,8 @@ def to_unicode(s):
102148
raise TypeError('You are required to pass either unicode or string here, not: %r (%s)' % (type(s), s))
103149
try:
104150
s = s.decode('utf-8')
105-
except UnicodeDecodeError, le:
151+
except UnicodeDecodeError:
152+
le = sys.exc_info()[1]
106153
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,))
107154
return s
108155

@@ -131,7 +178,8 @@ def to_unicode_optional_iterator(x):
131178

132179
try:
133180
l = list(x)
134-
except TypeError, e:
181+
except TypeError:
182+
e = sys.exc_info()[1]
135183
assert 'is not iterable' in str(e)
136184
return x
137185
else:
@@ -147,15 +195,18 @@ def to_utf8_optional_iterator(x):
147195

148196
try:
149197
l = list(x)
150-
except TypeError, e:
198+
except TypeError:
199+
e = sys.exc_info()[1]
151200
assert 'is not iterable' in str(e)
152201
return x
153202
else:
154203
return [ to_utf8_if_string(e) for e in l ]
155204

156205
def escape(s):
157206
"""Escape a URL including any /."""
158-
return urllib.quote(s.encode('utf-8'), safe='~')
207+
if not PY3:
208+
s = s.encode('utf-8')
209+
return quote(s, safe='~')
159210

160211
def generate_timestamp():
161212
"""Get seconds since epoch (UTC)."""
@@ -206,7 +257,7 @@ def __str__(self):
206257
data = {'oauth_consumer_key': self.key,
207258
'oauth_consumer_secret': self.secret}
208259

209-
return urllib.urlencode(data)
260+
return urlencode(data)
210261

211262

212263
class Token(object):
@@ -274,7 +325,7 @@ def to_string(self):
274325

275326
if self.callback_confirmed is not None:
276327
data['oauth_callback_confirmed'] = self.callback_confirmed
277-
return urllib.urlencode(data)
328+
return urlencode(data)
278329

279330
@staticmethod
280331
def from_string(s):
@@ -345,7 +396,7 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None,
345396
self.url = to_unicode(url)
346397
self.method = method
347398
if parameters is not None:
348-
for k, v in parameters.iteritems():
399+
for k, v in iteritems(parameters):
349400
k = to_unicode(k)
350401
v = to_unicode_optional_iterator(v)
351402
self[k] = v
@@ -382,7 +433,7 @@ def _get_timestamp_nonce(self):
382433

383434
def get_nonoauth_parameters(self):
384435
"""Get any non-OAuth parameters."""
385-
return dict([(k, v) for k, v in self.iteritems()
436+
return dict([(k, v) for k, v in iteritems(self)
386437
if not k.startswith('oauth_')])
387438

388439
def to_header(self, realm=''):
@@ -402,13 +453,13 @@ def to_header(self, realm=''):
402453
def to_postdata(self):
403454
"""Serialize as post data for a POST request."""
404455
d = {}
405-
for k, v in self.iteritems():
456+
for k, v in iteritems(self):
406457
d[k.encode('utf-8')] = to_utf8_optional_iterator(v)
407458

408459
# tell urlencode to deal with sequence values and map them correctly
409460
# to resulting querystring. for example self["k"] = ["v1", "v2"] will
410461
# result in 'k=v1&k=v2' and not k=%5B%27v1%27%2C+%27v2%27%5D
411-
return urllib.urlencode(d, True).replace('+', '%20')
462+
return urlencode(d, True).replace('+', '%20')
412463

413464
def to_url(self):
414465
"""Serialize as a URL for a GET request."""
@@ -437,7 +488,7 @@ def to_url(self):
437488
fragment = base_url[5]
438489

439490
url = (scheme, netloc, path, params,
440-
urllib.urlencode(query, True), fragment)
491+
urlencode(query, True), fragment)
441492
return urlparse.urlunparse(url)
442493

443494
def get_parameter(self, parameter):
@@ -450,7 +501,7 @@ def get_parameter(self, parameter):
450501
def get_normalized_parameters(self):
451502
"""Return a string that contains the parameters that must be signed."""
452503
items = []
453-
for key, value in self.iteritems():
504+
for key, value in iteritems(self):
454505
if key == 'oauth_signature':
455506
continue
456507
# 1.0a/9.1.1 states that kvp must be sorted by key, then by value,
@@ -460,7 +511,8 @@ def get_normalized_parameters(self):
460511
else:
461512
try:
462513
value = list(value)
463-
except TypeError, e:
514+
except TypeError:
515+
e = sys.exc_info()[1]
464516
assert 'is not iterable' in str(e)
465517
items.append((to_utf8_if_string(key), to_utf8_if_string(value)))
466518
else:
@@ -474,7 +526,7 @@ def get_normalized_parameters(self):
474526
items.extend(url_items)
475527

476528
items.sort()
477-
encoded_str = urllib.urlencode(items)
529+
encoded_str = urlencode(items)
478530
# Encode signature parameters per Oauth Core 1.0 protocol
479531
# spec draft 7, section 3.6
480532
# (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6)
@@ -490,7 +542,7 @@ def sign_request(self, signature_method, consumer, token):
490542
# section 4.1.1 "OAuth Consumers MUST NOT include an
491543
# oauth_body_hash parameter on requests with form-encoded
492544
# request bodies."
493-
self['oauth_body_hash'] = base64.b64encode(sha(self.body).digest())
545+
self['oauth_body_hash'] = s(base64.b64encode(sha(b(self.body)).digest()))
494546

495547
if 'oauth_consumer_key' not in self:
496548
self['oauth_consumer_key'] = consumer.key
@@ -600,15 +652,17 @@ def _split_header(header):
600652
# Split key-value.
601653
param_parts = param.split('=', 1)
602654
# Remove quotes and unescape the value.
603-
params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
655+
params[param_parts[0]] = unquote(param_parts[1].strip('\"'))
604656
return params
605657

606658
@staticmethod
607659
def _split_url_string(param_str):
608660
"""Turn URL string into parameters."""
609-
parameters = parse_qs(param_str.encode('utf-8'), keep_blank_values=True)
610-
for k, v in parameters.iteritems():
611-
parameters[k] = urllib.unquote(v[0])
661+
if not PY3:
662+
param_str = param_str.encode('utf8')
663+
parameters = parse_qs(param_str, keep_blank_values=True)
664+
for k, v in iteritems(parameters):
665+
parameters[k] = unquote(v[0])
612666
return parameters
613667

614668

@@ -661,12 +715,12 @@ def request(self, uri, method="GET", body='', headers=None,
661715

662716
req.sign_request(self.method, self.consumer, self.token)
663717

664-
schema, rest = urllib.splittype(uri)
718+
schema, rest = splittype(uri)
665719
if rest.startswith('//'):
666720
hierpart = '//'
667721
else:
668722
hierpart = ''
669-
host, rest = urllib.splithost(rest)
723+
host, rest = splithost(rest)
670724

671725
realm = schema + ':' + hierpart + host
672726

@@ -837,10 +891,10 @@ def sign(self, request, consumer, token):
837891
"""Builds the base signature string."""
838892
key, raw = self.signing_base(request, consumer, token)
839893

840-
hashed = hmac.new(key, raw, sha)
894+
hashed = hmac.new(b(key), b(raw), sha)
841895

842896
# Calculate the digest base 64.
843-
return binascii.b2a_base64(hashed.digest())[:-1]
897+
return s(binascii.b2a_base64(hashed.digest())[:-1])
844898

845899

846900
class SignatureMethod_PLAINTEXT(SignatureMethod):

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22
from setuptools import setup, find_packages
3-
import os, re
3+
import os, re, sys
44

55
PKG='oauth2'
66
VERSIONFILE = os.path.join('oauth2', '_version.py')
@@ -15,7 +15,7 @@
1515
if mo:
1616
mverstr = mo.group(1)
1717
else:
18-
print "unable to find version in %s" % (VERSIONFILE,)
18+
sys.stdout.write("unable to find version in %s\n" % (VERSIONFILE,))
1919
raise RuntimeError("if %s.py exists, it must be well-formed" % (VERSIONFILE,))
2020
AVSRE = r"^auto_build_num *= *['\"]([^'\"]*)['\"]"
2121
mo = re.search(AVSRE, verstrline, re.M)

0 commit comments

Comments
 (0)