Skip to content

Commit 517dbd8

Browse files
committed
Py3: compatible imports.
Also, drop use of 'iteritems'.
1 parent f8c0ece commit 517dbd8

File tree

3 files changed

+116
-117
lines changed

3 files changed

+116
-117
lines changed

oauth2/__init__.py

Lines changed: 50 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,23 @@
2323
"""
2424

2525
import base64
26-
import urllib
26+
from hashlib import sha1
2727
import time
2828
import random
29-
import urlparse
3029
import hmac
3130
import binascii
3231
import httplib2
3332

34-
try:
35-
from urlparse import parse_qs
36-
except ImportError: #pragma NO COVER
37-
# fall back for Python 2.5
38-
from cgi import parse_qs
39-
else: #pragma NO COVER
40-
parse_qs # placate pyflakes
41-
42-
try:
43-
from hashlib import sha1 as sha
44-
except ImportError: #pragma NO COVER
45-
# hashlib was added in Python 2.5
46-
import sha
47-
48-
import _version
49-
50-
__version__ = _version.__version__
33+
from ._compat import quote
34+
from ._compat import STRING_TYPES
35+
from ._compat import TEXT
36+
from ._compat import unquote
37+
from ._compat import unquote_to_bytes
38+
from ._compat import urlencode
39+
from ._compat import urlparse
40+
from ._compat import urlunparse
41+
from ._compat import parse_qs
42+
from ._version import __version__
5143

5244
OAUTH_VERSION = '1.0' # Hi Blaine!
5345
HTTP_METHOD = 'GET'
@@ -87,7 +79,7 @@ def build_xoauth_string(url, consumer, token=None):
8779
request.sign_request(signing_method, consumer, token)
8880

8981
params = []
90-
for k, v in sorted(request.iteritems()):
82+
for k, v in sorted(request.items()):
9183
if v is not None:
9284
params.append('%s="%s"' % (k, escape(v)))
9385

@@ -97,32 +89,33 @@ def build_xoauth_string(url, consumer, token=None):
9789
def to_unicode(s):
9890
""" Convert to unicode, raise exception with instructive error
9991
message if s is not unicode, ascii, or utf-8. """
100-
if not isinstance(s, unicode):
101-
if not isinstance(s, str):
92+
if not isinstance(s, TEXT):
93+
if not isinstance(s, bytes):
10294
raise TypeError('You are required to pass either unicode or '
103-
'string here, not: %r (%s)' % (type(s), s))
95+
'bytes here, not: %r (%s)' % (type(s), s))
10496
try:
10597
s = s.decode('utf-8')
10698
except UnicodeDecodeError as le:
10799
raise TypeError('You are required to pass either a unicode '
108-
'object or a utf-8 string here. You passed a '
109-
'Python string object which contained non-utf-8: '
110-
'%r. The UnicodeDecodeError that resulted from '
111-
'attempting to interpret it as utf-8 was: %s'
100+
'object or a utf-8-enccoded bytes string here. '
101+
'You passed a bytes object which contained '
102+
'non-utf-8: %r. The UnicodeDecodeError that '
103+
'resulted from attempting to interpret it as '
104+
'utf-8 was: %s'
112105
% (s, le,))
113106
return s
114107

115108
def to_utf8(s):
116109
return to_unicode(s).encode('utf-8')
117110

118111
def to_unicode_if_string(s):
119-
if isinstance(s, basestring):
112+
if isinstance(s, STRING_TYPES):
120113
return to_unicode(s)
121114
else:
122115
return s
123116

124117
def to_utf8_if_string(s):
125-
if isinstance(s, basestring):
118+
if isinstance(s, STRING_TYPES):
126119
return to_utf8(s)
127120
else:
128121
return s
@@ -132,7 +125,7 @@ def to_unicode_optional_iterator(x):
132125
Raise TypeError if x is a str containing non-utf8 bytes or if x is
133126
an iterable which contains such a str.
134127
"""
135-
if isinstance(x, basestring):
128+
if isinstance(x, STRING_TYPES):
136129
return to_unicode(x)
137130

138131
try:
@@ -148,7 +141,7 @@ def to_utf8_optional_iterator(x):
148141
Raise TypeError if x is a str or if x is an iterable which
149142
contains a str.
150143
"""
151-
if isinstance(x, basestring):
144+
if isinstance(x, STRING_TYPES):
152145
return to_utf8(x)
153146

154147
try:
@@ -161,7 +154,7 @@ def to_utf8_optional_iterator(x):
161154

162155
def escape(s):
163156
"""Escape a URL including any /."""
164-
return urllib.quote(s.encode('utf-8'), safe='~')
157+
return quote(s.encode('utf-8'), safe='~')
165158

166159
def generate_timestamp():
167160
"""Get seconds since epoch (UTC)."""
@@ -212,7 +205,7 @@ def __str__(self):
212205
data = {'oauth_consumer_key': self.key,
213206
'oauth_consumer_secret': self.secret}
214207

215-
return urllib.urlencode(data)
208+
return urlencode(data)
216209

217210

218211
class Token(object):
@@ -256,13 +249,13 @@ def set_verifier(self, verifier=None):
256249
def get_callback_url(self):
257250
if self.callback and self.verifier:
258251
# Append the oauth_verifier.
259-
parts = urlparse.urlparse(self.callback)
252+
parts = urlparse(self.callback)
260253
scheme, netloc, path, params, query, fragment = parts[:6]
261254
if query:
262255
query = '%s&oauth_verifier=%s' % (query, self.verifier)
263256
else:
264257
query = 'oauth_verifier=%s' % self.verifier
265-
return urlparse.urlunparse((scheme, netloc, path, params,
258+
return urlunparse((scheme, netloc, path, params,
266259
query, fragment))
267260
return self.callback
268261

@@ -280,7 +273,7 @@ def to_string(self):
280273

281274
if self.callback_confirmed is not None:
282275
data['oauth_callback_confirmed'] = self.callback_confirmed
283-
return urllib.urlencode(data)
276+
return urlencode(data)
284277

285278
@staticmethod
286279
def from_string(s):
@@ -351,7 +344,7 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None,
351344
self.url = to_unicode(url)
352345
self.method = method
353346
if parameters is not None:
354-
for k, v in parameters.iteritems():
347+
for k, v in parameters.items():
355348
k = to_unicode(k)
356349
v = to_unicode_optional_iterator(v)
357350
self[k] = v
@@ -363,8 +356,7 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None,
363356
def url(self, value):
364357
self.__dict__['url'] = value
365358
if value is not None:
366-
scheme, netloc, path, params, query, fragment = urlparse.urlparse(
367-
value)
359+
scheme, netloc, path, params, query, fragment = urlparse(value)
368360

369361
# Exclude default port numbers.
370362
if scheme == 'http' and netloc[-3:] == ':80':
@@ -375,7 +367,7 @@ def url(self, value):
375367
raise ValueError("Unsupported URL %s (%s)." % (value, scheme))
376368

377369
# Normalized URL excludes params, query, and fragment.
378-
self.normalized_url = urlparse.urlunparse(
370+
self.normalized_url = urlunparse(
379371
(scheme, netloc, path, None, None, None))
380372
else:
381373
self.normalized_url = None
@@ -390,7 +382,7 @@ def _get_timestamp_nonce(self):
390382

391383
def get_nonoauth_parameters(self):
392384
"""Get any non-OAuth parameters."""
393-
return dict([(k, v) for k, v in self.iteritems()
385+
return dict([(k, v) for k, v in self.items()
394386
if not k.startswith('oauth_')])
395387

396388
def to_header(self, realm=''):
@@ -410,17 +402,17 @@ def to_header(self, realm=''):
410402
def to_postdata(self):
411403
"""Serialize as post data for a POST request."""
412404
d = {}
413-
for k, v in self.iteritems():
405+
for k, v in self.items():
414406
d[k.encode('utf-8')] = to_utf8_optional_iterator(v)
415407

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

421413
def to_url(self):
422414
"""Serialize as a URL for a GET request."""
423-
base_url = urlparse.urlparse(self.url)
415+
base_url = urlparse(self.url)
424416
try:
425417
query = base_url.query
426418
except AttributeError: #pragma NO COVER
@@ -445,8 +437,8 @@ def to_url(self):
445437
fragment = base_url[5]
446438

447439
url = (scheme, netloc, path, params,
448-
urllib.urlencode(query, True), fragment)
449-
return urlparse.urlunparse(url)
440+
urlencode(query, True), fragment)
441+
return urlunparse(url)
450442

451443
def get_parameter(self, parameter):
452444
ret = self.get(parameter)
@@ -458,12 +450,12 @@ def get_parameter(self, parameter):
458450
def get_normalized_parameters(self):
459451
"""Return a string that contains the parameters that must be signed."""
460452
items = []
461-
for key, value in self.iteritems():
453+
for key, value in self.items():
462454
if key == 'oauth_signature':
463455
continue
464456
# 1.0a/9.1.1 states that kvp must be sorted by key, then by value,
465457
# so we unpack sequence values into multiple items for sorting.
466-
if isinstance(value, basestring):
458+
if isinstance(value, STRING_TYPES):
467459
items.append((to_utf8_if_string(key), to_utf8(value)))
468460
else:
469461
try:
@@ -478,15 +470,15 @@ def get_normalized_parameters(self):
478470
for item in value)
479471

480472
# Include any query string parameters from the provided URL
481-
query = urlparse.urlparse(self.url)[4]
473+
query = urlparse(self.url)[4]
482474

483475
url_items = self._split_url_string(query).items()
484476
url_items = [(to_utf8(k), to_utf8(v))
485477
for k, v in url_items if k != 'oauth_signature' ]
486478
items.extend(url_items)
487479

488480
items.sort()
489-
encoded_str = urllib.urlencode(items)
481+
encoded_str = urlencode(items)
490482
# Encode signature parameters per Oauth Core 1.0 protocol
491483
# spec draft 7, section 3.6
492484
# (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6)
@@ -503,7 +495,7 @@ def sign_request(self, signature_method, consumer, token):
503495
# section 4.1.1 "OAuth Consumers MUST NOT include an
504496
# oauth_body_hash parameter on requests with form-encoded
505497
# request bodies."
506-
self['oauth_body_hash'] = base64.b64encode(sha(self.body).digest())
498+
self['oauth_body_hash'] = base64.b64encode(sha1(self.body).digest())
507499

508500
if 'oauth_consumer_key' not in self:
509501
self['oauth_consumer_key'] = consumer.key
@@ -551,7 +543,7 @@ def from_request(cls, http_method, http_url, headers=None, parameters=None,
551543
parameters.update(query_params)
552544

553545
# URL parameters.
554-
param_str = urlparse.urlparse(http_url)[4] # query
546+
param_str = urlparse(http_url)[4] # query
555547
url_params = cls._split_url_string(param_str)
556548
parameters.update(url_params)
557549

@@ -613,16 +605,16 @@ def _split_header(header):
613605
# Split key-value.
614606
param_parts = param.split('=', 1)
615607
# Remove quotes and unescape the value.
616-
params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"'))
608+
params[param_parts[0]] = unquote(param_parts[1].strip('\"'))
617609
return params
618610

619611
@staticmethod
620612
def _split_url_string(param_str):
621613
"""Turn URL string into parameters."""
622614
parameters = parse_qs(param_str.encode('utf-8'),
623615
keep_blank_values=True)
624-
for k, v in parameters.iteritems():
625-
parameters[k] = urllib.unquote(v[0])
616+
for k, v in parameters.items():
617+
parameters[k] = unquote_to_bytes(v[0])
626618
return parameters
627619

628620

@@ -676,8 +668,8 @@ def request(self, uri, method="GET", body='', headers=None,
676668

677669
req.sign_request(self.method, self.consumer, self.token)
678670

679-
scheme, netloc, path, params, query, fragment = urlparse.urlparse(uri)
680-
realm = urlparse.urlunparse((scheme, netloc, '', None, None, None))
671+
scheme, netloc, path, params, query, fragment = urlparse(uri)
672+
realm = urlunparse((scheme, netloc, '', None, None, None))
681673

682674
if is_form_encoded:
683675
body = req.to_postdata()
@@ -842,7 +834,7 @@ def sign(self, request, consumer, token):
842834
"""Builds the base signature string."""
843835
key, raw = self.signing_base(request, consumer, token)
844836

845-
hashed = hmac.new(key, raw, sha)
837+
hashed = hmac.new(key, raw, sha1)
846838

847839
# Calculate the digest base 64.
848840
return binascii.b2a_base64(hashed.digest())[:-1]

oauth2/_compat.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
except NameError: #pragma NO COVER Py3k
44
TEXT = str
55
STRING_TYPES = (str, bytes)
6-
else:
6+
else: #pragma NO COVER Python2
77
STRING_TYPES = (unicode, bytes)
88

99
def u(x, encoding='ascii'):
@@ -13,3 +13,24 @@ def u(x, encoding='ascii'):
1313
return x.decode(encoding)
1414
except AttributeError:
1515
raise ValueError('WTF: %s' % x)
16+
17+
try:
18+
import urlparse
19+
except ImportError: #pragma NO COVER Py3k
20+
from urllib.parse import parse_qs
21+
from urllib.parse import parse_qsl
22+
from urllib.parse import quote
23+
from urllib.parse import unquote
24+
from urllib.parse import unquote_to_bytes
25+
from urllib.parse import urlencode
26+
from urllib.parse import urlparse
27+
from urllib.parse import urlunparse
28+
else: #pragma NO COVER Python2
29+
from urlparse import parse_qs
30+
from urlparse import parse_qsl
31+
from urllib import quote
32+
from urllib import unquote
33+
from urllib import urlencode
34+
from urlparse import urlparse
35+
from urlparse import urlunparse
36+
unquote_to_bytes = unquote

0 commit comments

Comments
 (0)