26
26
import urllib
27
27
import time
28
28
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
30
43
import hmac
31
44
import binascii
32
45
import httplib2
46
+ import sys
33
47
34
48
try :
35
- from urlparse import parse_qs
36
- parse_qs # placate pyflakes
49
+ from urllib .parse import parse_qs
37
50
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
40
57
41
58
try :
42
59
from hashlib import sha1
52
69
OAUTH_VERSION = '1.0' # Hi Blaine!
53
70
HTTP_METHOD = 'GET'
54
71
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 ()
55
101
56
102
57
103
class Error (RuntimeError ):
@@ -87,7 +133,7 @@ def build_xoauth_string(url, consumer, token=None):
87
133
request .sign_request (signing_method , consumer , token )
88
134
89
135
params = []
90
- for k , v in sorted (request . iteritems ()):
136
+ for k , v in sorted (iteritems (request )):
91
137
if v is not None :
92
138
params .append ('%s="%s"' % (k , escape (v )))
93
139
@@ -102,7 +148,8 @@ def to_unicode(s):
102
148
raise TypeError ('You are required to pass either unicode or string here, not: %r (%s)' % (type (s ), s ))
103
149
try :
104
150
s = s .decode ('utf-8' )
105
- except UnicodeDecodeError , le :
151
+ except UnicodeDecodeError :
152
+ le = sys .exc_info ()[1 ]
106
153
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 ,))
107
154
return s
108
155
@@ -131,7 +178,8 @@ def to_unicode_optional_iterator(x):
131
178
132
179
try :
133
180
l = list (x )
134
- except TypeError , e :
181
+ except TypeError :
182
+ e = sys .exc_info ()[1 ]
135
183
assert 'is not iterable' in str (e )
136
184
return x
137
185
else :
@@ -147,15 +195,18 @@ def to_utf8_optional_iterator(x):
147
195
148
196
try :
149
197
l = list (x )
150
- except TypeError , e :
198
+ except TypeError :
199
+ e = sys .exc_info ()[1 ]
151
200
assert 'is not iterable' in str (e )
152
201
return x
153
202
else :
154
203
return [ to_utf8_if_string (e ) for e in l ]
155
204
156
205
def escape (s ):
157
206
"""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 = '~' )
159
210
160
211
def generate_timestamp ():
161
212
"""Get seconds since epoch (UTC)."""
@@ -206,7 +257,7 @@ def __str__(self):
206
257
data = {'oauth_consumer_key' : self .key ,
207
258
'oauth_consumer_secret' : self .secret }
208
259
209
- return urllib . urlencode (data )
260
+ return urlencode (data )
210
261
211
262
212
263
class Token (object ):
@@ -274,7 +325,7 @@ def to_string(self):
274
325
275
326
if self .callback_confirmed is not None :
276
327
data ['oauth_callback_confirmed' ] = self .callback_confirmed
277
- return urllib . urlencode (data )
328
+ return urlencode (data )
278
329
279
330
@staticmethod
280
331
def from_string (s ):
@@ -345,7 +396,7 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None,
345
396
self .url = to_unicode (url )
346
397
self .method = method
347
398
if parameters is not None :
348
- for k , v in parameters . iteritems ():
399
+ for k , v in iteritems (parameters ):
349
400
k = to_unicode (k )
350
401
v = to_unicode_optional_iterator (v )
351
402
self [k ] = v
@@ -382,7 +433,7 @@ def _get_timestamp_nonce(self):
382
433
383
434
def get_nonoauth_parameters (self ):
384
435
"""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 )
386
437
if not k .startswith ('oauth_' )])
387
438
388
439
def to_header (self , realm = '' ):
@@ -402,13 +453,13 @@ def to_header(self, realm=''):
402
453
def to_postdata (self ):
403
454
"""Serialize as post data for a POST request."""
404
455
d = {}
405
- for k , v in self . iteritems ():
456
+ for k , v in iteritems (self ):
406
457
d [k .encode ('utf-8' )] = to_utf8_optional_iterator (v )
407
458
408
459
# tell urlencode to deal with sequence values and map them correctly
409
460
# to resulting querystring. for example self["k"] = ["v1", "v2"] will
410
461
# 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' )
412
463
413
464
def to_url (self ):
414
465
"""Serialize as a URL for a GET request."""
@@ -437,7 +488,7 @@ def to_url(self):
437
488
fragment = base_url [5 ]
438
489
439
490
url = (scheme , netloc , path , params ,
440
- urllib . urlencode (query , True ), fragment )
491
+ urlencode (query , True ), fragment )
441
492
return urlparse .urlunparse (url )
442
493
443
494
def get_parameter (self , parameter ):
@@ -450,7 +501,7 @@ def get_parameter(self, parameter):
450
501
def get_normalized_parameters (self ):
451
502
"""Return a string that contains the parameters that must be signed."""
452
503
items = []
453
- for key , value in self . iteritems ():
504
+ for key , value in iteritems (self ):
454
505
if key == 'oauth_signature' :
455
506
continue
456
507
# 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):
460
511
else :
461
512
try :
462
513
value = list (value )
463
- except TypeError , e :
514
+ except TypeError :
515
+ e = sys .exc_info ()[1 ]
464
516
assert 'is not iterable' in str (e )
465
517
items .append ((to_utf8_if_string (key ), to_utf8_if_string (value )))
466
518
else :
@@ -474,7 +526,7 @@ def get_normalized_parameters(self):
474
526
items .extend (url_items )
475
527
476
528
items .sort ()
477
- encoded_str = urllib . urlencode (items )
529
+ encoded_str = urlencode (items )
478
530
# Encode signature parameters per Oauth Core 1.0 protocol
479
531
# spec draft 7, section 3.6
480
532
# (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6)
@@ -490,7 +542,7 @@ def sign_request(self, signature_method, consumer, token):
490
542
# section 4.1.1 "OAuth Consumers MUST NOT include an
491
543
# oauth_body_hash parameter on requests with form-encoded
492
544
# 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 () ))
494
546
495
547
if 'oauth_consumer_key' not in self :
496
548
self ['oauth_consumer_key' ] = consumer .key
@@ -600,15 +652,17 @@ def _split_header(header):
600
652
# Split key-value.
601
653
param_parts = param .split ('=' , 1 )
602
654
# 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 ('\" ' ))
604
656
return params
605
657
606
658
@staticmethod
607
659
def _split_url_string (param_str ):
608
660
"""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 ])
612
666
return parameters
613
667
614
668
@@ -661,12 +715,12 @@ def request(self, uri, method="GET", body='', headers=None,
661
715
662
716
req .sign_request (self .method , self .consumer , self .token )
663
717
664
- schema , rest = urllib . splittype (uri )
718
+ schema , rest = splittype (uri )
665
719
if rest .startswith ('//' ):
666
720
hierpart = '//'
667
721
else :
668
722
hierpart = ''
669
- host , rest = urllib . splithost (rest )
723
+ host , rest = splithost (rest )
670
724
671
725
realm = schema + ':' + hierpart + host
672
726
@@ -837,10 +891,10 @@ def sign(self, request, consumer, token):
837
891
"""Builds the base signature string."""
838
892
key , raw = self .signing_base (request , consumer , token )
839
893
840
- hashed = hmac .new (key , raw , sha )
894
+ hashed = hmac .new (b ( key ), b ( raw ) , sha )
841
895
842
896
# Calculate the digest base 64.
843
- return binascii .b2a_base64 (hashed .digest ())[:- 1 ]
897
+ return s ( binascii .b2a_base64 (hashed .digest ())[:- 1 ])
844
898
845
899
846
900
class SignatureMethod_PLAINTEXT (SignatureMethod ):
0 commit comments