26
26
import urllib
27
27
import time
28
28
import random
29
- import urlparse
30
29
import hmac
31
30
import binascii
32
31
import httplib2
33
32
33
+ try :
34
+ import urlparse
35
+ except ImportError :
36
+ # urlparse location changed in python 3
37
+ from urllib import parse as urlparse
38
+
34
39
try :
35
40
from urlparse import parse_qs
36
41
parse_qs # placate pyflakes
39
44
from cgi import parse_qs
40
45
41
46
try :
42
- from hashlib import sha1
43
- sha = sha1
47
+ from hashlib import sha1 as sha
44
48
except ImportError :
45
49
# hashlib was added in Python 2.5
46
50
import sha
49
53
50
54
__version__ = _version .__version__
51
55
52
- OAUTH_VERSION = '1.0' # Hi Blaine!
56
+ OAUTH_VERSION = '1.0'
53
57
HTTP_METHOD = 'GET'
54
58
SIGNATURE_METHOD = 'PLAINTEXT'
55
59
@@ -87,7 +91,7 @@ def build_xoauth_string(url, consumer, token=None):
87
91
request .sign_request (signing_method , consumer , token )
88
92
89
93
params = []
90
- for k , v in sorted (request .iteritems ()):
94
+ for k , v in sorted (request .items ()):
91
95
if v is not None :
92
96
params .append ('%s="%s"' % (k , escape (v )))
93
97
@@ -97,41 +101,66 @@ def build_xoauth_string(url, consumer, token=None):
97
101
def to_unicode (s ):
98
102
""" Convert to unicode, raise exception with instructive error
99
103
message if s is not unicode, ascii, or utf-8. """
100
- if not isinstance (s , unicode ):
104
+ # Python 3 strings are unicode (utf-8) by default
105
+ try :
106
+ if not isinstance (s , unicode ):
107
+ if not isinstance (s , str ):
108
+ raise TypeError ('You are required to pass either unicode or string here, not: %r (%s)' % (type (s ), s ))
109
+ try :
110
+ s = s .decode ('utf-8' )
111
+ except UnicodeDecodeError as le :
112
+ 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 ,))
113
+ except NameError :
101
114
if not isinstance (s , str ):
102
115
raise TypeError ('You are required to pass either unicode or string here, not: %r (%s)' % (type (s ), s ))
103
116
try :
104
- s = s .decode ('utf-8' )
105
- except UnicodeDecodeError , le :
117
+ s = s .encode ('utf-8' )
118
+ except UnicodeDecodeError as le :
106
119
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
120
return s
108
121
109
122
def to_utf8 (s ):
110
123
return to_unicode (s ).encode ('utf-8' )
111
124
112
125
def to_unicode_if_string (s ):
113
- if isinstance (s , basestring ):
114
- return to_unicode (s )
115
- else :
116
- return s
126
+ try :
127
+ if isinstance (s , basestring ):
128
+ return to_unicode (s )
129
+ else :
130
+ return s
131
+ except NameError :
132
+ if isinstance (s , str ):
133
+ return to_unicode (s )
134
+ else :
135
+ return s
117
136
118
137
def to_utf8_if_string (s ):
119
- if isinstance (s , basestring ):
120
- return to_utf8 (s )
121
- else :
122
- return s
138
+ try :
139
+ if isinstance (s , basestring ):
140
+ return to_utf8 (s )
141
+ else :
142
+ return s
143
+ except NameError :
144
+ if isinstance (s , str ):
145
+ return to_utf8 (s )
146
+ else :
147
+ return s
123
148
124
149
def to_unicode_optional_iterator (x ):
125
150
"""
126
151
Raise TypeError if x is a str containing non-utf8 bytes or if x is
127
152
an iterable which contains such a str.
128
153
"""
129
- if isinstance (x , basestring ):
130
- return to_unicode (x )
154
+ try :
155
+ if isinstance (x , basestring ):
156
+ return to_unicode (x )
157
+ except NameError :
158
+ if isinstance (x , str ):
159
+ return to_unicode (x )
131
160
132
161
try :
133
162
l = list (x )
134
- except TypeError , e :
163
+ except TypeError as e :
135
164
assert 'is not iterable' in str (e )
136
165
return x
137
166
else :
@@ -142,20 +171,27 @@ def to_utf8_optional_iterator(x):
142
171
Raise TypeError if x is a str or if x is an iterable which
143
172
contains a str.
144
173
"""
145
- if isinstance (x , basestring ):
146
- return to_utf8 (x )
174
+ try :
175
+ if isinstance (x , basestring ):
176
+ return to_utf8 (x )
177
+ except NameError :
178
+ if isinstance (x , str ):
179
+ return to_utf8 (x )
147
180
148
181
try :
149
182
l = list (x )
150
- except TypeError , e :
183
+ except TypeError as e :
151
184
assert 'is not iterable' in str (e )
152
185
return x
153
186
else :
154
187
return [ to_utf8_if_string (e ) for e in l ]
155
188
156
189
def escape (s ):
157
190
"""Escape a URL including any /."""
158
- return urllib .quote (s .encode ('utf-8' ), safe = '~' )
191
+ try :
192
+ return urllib .quote (s .encode ('utf-8' ), safe = '~' )
193
+ except AttributeError :
194
+ return urlparse .quote (s .encode ('utf-8' ), safe = '~' )
159
195
160
196
def generate_timestamp ():
161
197
"""Get seconds since epoch (UTC)."""
@@ -205,8 +241,10 @@ def __init__(self, key, secret):
205
241
def __str__ (self ):
206
242
data = {'oauth_consumer_key' : self .key ,
207
243
'oauth_consumer_secret' : self .secret }
208
-
209
- return urllib .urlencode (data )
244
+ try :
245
+ return urllib .urlencode (data )
246
+ except AttributeError :
247
+ return urlparse .urlencode (data )
210
248
211
249
212
250
class Token (object ):
@@ -274,7 +312,10 @@ def to_string(self):
274
312
275
313
if self .callback_confirmed is not None :
276
314
data ['oauth_callback_confirmed' ] = self .callback_confirmed
277
- return urllib .urlencode (data )
315
+ try :
316
+ return urllib .urlencode (data )
317
+ except AttributeError :
318
+ return urlparse .urlencode (data )
278
319
279
320
@staticmethod
280
321
def from_string (s ):
@@ -345,7 +386,7 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None,
345
386
self .url = to_unicode (url )
346
387
self .method = method
347
388
if parameters is not None :
348
- for k , v in parameters .iteritems ():
389
+ for k , v in parameters .items ():
349
390
k = to_unicode (k )
350
391
v = to_unicode_optional_iterator (v )
351
392
self [k ] = v
@@ -382,7 +423,7 @@ def _get_timestamp_nonce(self):
382
423
383
424
def get_nonoauth_parameters (self ):
384
425
"""Get any non-OAuth parameters."""
385
- return dict ([(k , v ) for k , v in self .iteritems ()
426
+ return dict ([(k , v ) for k , v in self .items ()
386
427
if not k .startswith ('oauth_' )])
387
428
388
429
def to_header (self , realm = '' ):
@@ -402,13 +443,16 @@ def to_header(self, realm=''):
402
443
def to_postdata (self ):
403
444
"""Serialize as post data for a POST request."""
404
445
d = {}
405
- for k , v in self .iteritems ():
446
+ for k , v in self .items ():
406
447
d [k .encode ('utf-8' )] = to_utf8_optional_iterator (v )
407
448
408
449
# tell urlencode to deal with sequence values and map them correctly
409
450
# to resulting querystring. for example self["k"] = ["v1", "v2"] will
410
451
# result in 'k=v1&k=v2' and not k=%5B%27v1%27%2C+%27v2%27%5D
411
- return urllib .urlencode (d , True ).replace ('+' , '%20' )
452
+ try :
453
+ return urllib .urlencode (d , True ).replace ('+' , '%20' )
454
+ except AttributeError :
455
+ return urlparse .urlencode (d , True ).replace ('+' , '%20' )
412
456
413
457
def to_url (self ):
414
458
"""Serialize as a URL for a GET request."""
@@ -430,15 +474,20 @@ def to_url(self):
430
474
fragment = to_utf8 (base_url .fragment )
431
475
except AttributeError :
432
476
# must be python <2.5
433
- scheme = to_utf8 (base_url [0 ])
434
- netloc = to_utf8 (base_url [1 ])
435
- path = to_utf8 (base_url [2 ])
436
- params = to_utf8 (base_url [3 ])
437
- fragment = to_utf8 (base_url [5 ])
438
-
439
- url = (scheme , netloc , path , params ,
440
- urllib .urlencode (query , True ), fragment )
441
- return urlparse .urlunparse (url )
477
+ scheme = base_url [0 ]
478
+ netloc = base_url [1 ]
479
+ path = base_url [2 ]
480
+ params = base_url [3 ]
481
+ fragment = base_url [5 ]
482
+
483
+ try :
484
+ url = (scheme , netloc , path , params ,
485
+ urllib .urlencode (query , True ), fragment )
486
+ return urllib .urlunparse (url )
487
+ except AttributeError :
488
+ url = (scheme , netloc , path , params ,
489
+ urlparse .urlencode (query , True ), fragment )
490
+ return urlparse .urlunparse (url )
442
491
443
492
def get_parameter (self , parameter ):
444
493
ret = self .get (parameter )
@@ -450,21 +499,33 @@ def get_parameter(self, parameter):
450
499
def get_normalized_parameters (self ):
451
500
"""Return a string that contains the parameters that must be signed."""
452
501
items = []
453
- for key , value in self .iteritems ():
502
+ for key , value in self .items ():
454
503
if key == 'oauth_signature' :
455
504
continue
456
505
# 1.0a/9.1.1 states that kvp must be sorted by key, then by value,
457
506
# so we unpack sequence values into multiple items for sorting.
458
- if isinstance (value , basestring ):
459
- items .append ((to_utf8_if_string (key ), to_utf8 (value )))
460
- else :
461
- try :
462
- value = list (value )
463
- except TypeError , e :
464
- assert 'is not iterable' in str (e )
465
- items .append ((to_utf8_if_string (key ), to_utf8_if_string (value )))
507
+ try :
508
+ if isinstance (value , basestring ):
509
+ items .append ((to_utf8_if_string (key ), to_utf8 (value )))
466
510
else :
467
- items .extend ((to_utf8_if_string (key ), to_utf8_if_string (item )) for item in value )
511
+ try :
512
+ value = list (value )
513
+ except TypeError as e :
514
+ assert 'is not iterable' in str (e )
515
+ items .append ((to_utf8_if_string (key ), to_utf8_if_string (value )))
516
+ else :
517
+ items .extend ((to_utf8_if_string (key ), to_utf8_if_string (item )) for item in value )
518
+ except NameError :
519
+ if isinstance (value , str ):
520
+ items .append ((to_utf8_if_string (key ), to_utf8 (value )))
521
+ else :
522
+ try :
523
+ value = list (value )
524
+ except TypeError as e :
525
+ assert 'is not iterable' in str (e )
526
+ items .append ((to_utf8_if_string (key ), to_utf8_if_string (value )))
527
+ else :
528
+ items .extend ((to_utf8_if_string (key ), to_utf8_if_string (item )) for item in value )
468
529
469
530
# Include any query string parameters from the provided URL
470
531
query = urlparse .urlparse (self .url )[4 ]
@@ -475,6 +536,10 @@ def get_normalized_parameters(self):
475
536
476
537
items .sort ()
477
538
encoded_str = urllib .urlencode (items , True )
539
+ try :
540
+ encoded_str = urllib .urlencode (items )
541
+ except AttributeError :
542
+ encoded_str = urlparse .urlencode (items )
478
543
# Encode signature parameters per Oauth Core 1.0 protocol
479
544
# spec draft 7, section 3.6
480
545
# (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6)
@@ -490,7 +555,7 @@ def sign_request(self, signature_method, consumer, token):
490
555
# section 4.1.1 "OAuth Consumers MUST NOT include an
491
556
# oauth_body_hash parameter on requests with form-encoded
492
557
# request bodies."
493
- self ['oauth_body_hash' ] = base64 .b64encode (sha (self .body ).digest ())
558
+ self ['oauth_body_hash' ] = base64 .b64encode (sha (self .body . encode ( "utf-8" ) ).digest ())
494
559
495
560
if 'oauth_consumer_key' not in self :
496
561
self ['oauth_consumer_key' ] = consumer .key
@@ -605,7 +670,10 @@ def _split_header(header):
605
670
# Split key-value.
606
671
param_parts = param .split ('=' , 1 )
607
672
# Remove quotes and unescape the value.
608
- params [param_parts [0 ]] = urllib .unquote (param_parts [1 ].strip ('\" ' ))
673
+ try :
674
+ params [param_parts [0 ]] = urllib .unquote (param_parts [1 ].strip ('\" ' ))
675
+ except AttributeError :
676
+ params [param_parts [0 ]] = urlparse .unquote (param_parts [1 ].strip ('\" ' ))
609
677
return params
610
678
611
679
@staticmethod
@@ -667,13 +735,19 @@ def request(self, uri, method="GET", body='', headers=None,
667
735
parameters = parameters , body = body , is_form_encoded = is_form_encoded )
668
736
669
737
req .sign_request (self .method , self .consumer , self .token )
670
-
671
- schema , rest = urllib .splittype (uri )
738
+
739
+ try :
740
+ schema , rest = urllib .splittype (uri )
741
+ except AttributeError :
742
+ schema , rest = urlparse .splittype (uri )
672
743
if rest .startswith ('//' ):
673
744
hierpart = '//'
674
745
else :
675
746
hierpart = ''
676
- host , rest = urllib .splithost (rest )
747
+ try :
748
+ host , rest = urllib .splithost (rest )
749
+ except AttributeError :
750
+ host , rest = urlparse .splithost (rest )
677
751
678
752
realm = schema + ':' + hierpart + host
679
753
@@ -844,7 +918,7 @@ def sign(self, request, consumer, token):
844
918
"""Builds the base signature string."""
845
919
key , raw = self .signing_base (request , consumer , token )
846
920
847
- hashed = hmac .new (key , raw , sha )
921
+ hashed = hmac .new (key . encode ( 'utf-8' ) , raw . encode ( 'utf-8' ) , sha )
848
922
849
923
# Calculate the digest base 64.
850
924
return binascii .b2a_base64 (hashed .digest ())[:- 1 ]
0 commit comments