2626import urllib
2727import time
2828import 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
3043import hmac
3144import binascii
3245import httplib2
46+ import sys
3347
3448try :
35- from urlparse import parse_qs
36- parse_qs # placate pyflakes
49+ from urllib .parse import parse_qs
3750except 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
4158try :
4259 from hashlib import sha1
5269OAUTH_VERSION = '1.0' # Hi Blaine!
5370HTTP_METHOD = 'GET'
5471SIGNATURE_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
57103class 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
156205def 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
160211def 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
212263class 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
846900class SignatureMethod_PLAINTEXT (SignatureMethod ):
0 commit comments