-
Notifications
You must be signed in to change notification settings - Fork 911
Python 3 support #125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Python 3 support #125
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,17 +26,34 @@ | |
import urllib | ||
import time | ||
import random | ||
import urlparse | ||
try: | ||
import urllib.parse as urlparse | ||
from urllib.parse import quote | ||
from urllib.parse import splithost | ||
from urllib.parse import splittype | ||
from urllib.parse import unquote | ||
from urllib.parse import urlencode | ||
except ImportError: | ||
import urlparse | ||
from urllib import quote | ||
from urllib import splithost | ||
from urllib import splittype | ||
from urllib import unquote | ||
from urllib import urlencode | ||
import hmac | ||
import binascii | ||
import httplib2 | ||
import sys | ||
|
||
try: | ||
from urlparse import parse_qs | ||
parse_qs # placate pyflakes | ||
from urllib.parse import parse_qs | ||
except ImportError: | ||
# fall back for Python 2.5 | ||
from cgi import parse_qs | ||
try: | ||
from urlparse import parse_qs | ||
parse_qs # placate pyflakes | ||
except ImportError: | ||
# fall back for Python 2.5 | ||
from cgi import parse_qs | ||
|
||
try: | ||
from hashlib import sha1 | ||
|
@@ -52,6 +69,35 @@ | |
OAUTH_VERSION = '1.0' # Hi Blaine! | ||
HTTP_METHOD = 'GET' | ||
SIGNATURE_METHOD = 'PLAINTEXT' | ||
PY3 = sys.version > '3' | ||
|
||
|
||
try: | ||
basestring | ||
except NameError: | ||
basestring = str | ||
try: | ||
unicode | ||
except NameError: | ||
unicode = str | ||
|
||
|
||
def b(string): | ||
if PY3: | ||
return string.encode('ascii') | ||
return string | ||
|
||
|
||
def s(bytes): | ||
if PY3: | ||
return bytes.decode('ascii') | ||
return bytes | ||
|
||
|
||
def iteritems(d): | ||
if PY3: | ||
return d.items() | ||
return d.iteritems() | ||
|
||
|
||
class Error(RuntimeError): | ||
|
@@ -87,7 +133,7 @@ def build_xoauth_string(url, consumer, token=None): | |
request.sign_request(signing_method, consumer, token) | ||
|
||
params = [] | ||
for k, v in sorted(request.iteritems()): | ||
for k, v in sorted(iteritems(request)): | ||
if v is not None: | ||
params.append('%s="%s"' % (k, escape(v))) | ||
|
||
|
@@ -102,7 +148,8 @@ def to_unicode(s): | |
raise TypeError('You are required to pass either unicode or string here, not: %r (%s)' % (type(s), s)) | ||
try: | ||
s = s.decode('utf-8') | ||
except UnicodeDecodeError, le: | ||
except UnicodeDecodeError: | ||
le = sys.exc_info()[1] | ||
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,)) | ||
return s | ||
|
||
|
@@ -131,7 +178,8 @@ def to_unicode_optional_iterator(x): | |
|
||
try: | ||
l = list(x) | ||
except TypeError, e: | ||
except TypeError: | ||
e = sys.exc_info()[1] | ||
assert 'is not iterable' in str(e) | ||
return x | ||
else: | ||
|
@@ -147,15 +195,18 @@ def to_utf8_optional_iterator(x): | |
|
||
try: | ||
l = list(x) | ||
except TypeError, e: | ||
except TypeError: | ||
e = sys.exc_info()[1] | ||
assert 'is not iterable' in str(e) | ||
return x | ||
else: | ||
return [ to_utf8_if_string(e) for e in l ] | ||
|
||
def escape(s): | ||
"""Escape a URL including any /.""" | ||
return urllib.quote(s.encode('utf-8'), safe='~') | ||
if not PY3: | ||
s = s.encode('utf-8') | ||
return quote(s, safe='~') | ||
|
||
def generate_timestamp(): | ||
"""Get seconds since epoch (UTC).""" | ||
|
@@ -206,7 +257,7 @@ def __str__(self): | |
data = {'oauth_consumer_key': self.key, | ||
'oauth_consumer_secret': self.secret} | ||
|
||
return urllib.urlencode(data) | ||
return urlencode(data) | ||
|
||
|
||
class Token(object): | ||
|
@@ -274,7 +325,7 @@ def to_string(self): | |
|
||
if self.callback_confirmed is not None: | ||
data['oauth_callback_confirmed'] = self.callback_confirmed | ||
return urllib.urlencode(data) | ||
return urlencode(data) | ||
|
||
@staticmethod | ||
def from_string(s): | ||
|
@@ -345,7 +396,7 @@ def __init__(self, method=HTTP_METHOD, url=None, parameters=None, | |
self.url = to_unicode(url) | ||
self.method = method | ||
if parameters is not None: | ||
for k, v in parameters.iteritems(): | ||
for k, v in iteritems(parameters): | ||
k = to_unicode(k) | ||
v = to_unicode_optional_iterator(v) | ||
self[k] = v | ||
|
@@ -382,7 +433,7 @@ def _get_timestamp_nonce(self): | |
|
||
def get_nonoauth_parameters(self): | ||
"""Get any non-OAuth parameters.""" | ||
return dict([(k, v) for k, v in self.iteritems() | ||
return dict([(k, v) for k, v in iteritems(self) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
if not k.startswith('oauth_')]) | ||
|
||
def to_header(self, realm=''): | ||
|
@@ -402,13 +453,13 @@ def to_header(self, realm=''): | |
def to_postdata(self): | ||
"""Serialize as post data for a POST request.""" | ||
d = {} | ||
for k, v in self.iteritems(): | ||
for k, v in iteritems(self): | ||
d[k.encode('utf-8')] = to_utf8_optional_iterator(v) | ||
|
||
# tell urlencode to deal with sequence values and map them correctly | ||
# to resulting querystring. for example self["k"] = ["v1", "v2"] will | ||
# result in 'k=v1&k=v2' and not k=%5B%27v1%27%2C+%27v2%27%5D | ||
return urllib.urlencode(d, True).replace('+', '%20') | ||
return urlencode(d, True).replace('+', '%20') | ||
|
||
def to_url(self): | ||
"""Serialize as a URL for a GET request.""" | ||
|
@@ -437,7 +488,7 @@ def to_url(self): | |
fragment = to_utf8(base_url[5]) | ||
|
||
url = (scheme, netloc, path, params, | ||
urllib.urlencode(query, True), fragment) | ||
urlencode(query, True), fragment) | ||
return urlparse.urlunparse(url) | ||
|
||
def get_parameter(self, parameter): | ||
|
@@ -450,7 +501,7 @@ def get_parameter(self, parameter): | |
def get_normalized_parameters(self): | ||
"""Return a string that contains the parameters that must be signed.""" | ||
items = [] | ||
for key, value in self.iteritems(): | ||
for key, value in iteritems(self): | ||
if key == 'oauth_signature': | ||
continue | ||
# 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): | |
else: | ||
try: | ||
value = list(value) | ||
except TypeError, e: | ||
except TypeError: | ||
e = sys.exc_info()[1] | ||
assert 'is not iterable' in str(e) | ||
items.append((to_utf8_if_string(key), to_utf8_if_string(value))) | ||
else: | ||
|
@@ -474,7 +526,7 @@ def get_normalized_parameters(self): | |
items.extend(url_items) | ||
|
||
items.sort() | ||
encoded_str = urllib.urlencode(items, True) | ||
encoded_str = urlencode(items, True) | ||
# Encode signature parameters per Oauth Core 1.0 protocol | ||
# spec draft 7, section 3.6 | ||
# (http://tools.ietf.org/html/draft-hammer-oauth-07#section-3.6) | ||
|
@@ -490,7 +542,7 @@ def sign_request(self, signature_method, consumer, token): | |
# section 4.1.1 "OAuth Consumers MUST NOT include an | ||
# oauth_body_hash parameter on requests with form-encoded | ||
# request bodies." | ||
self['oauth_body_hash'] = base64.b64encode(sha(self.body).digest()) | ||
self['oauth_body_hash'] = s(base64.b64encode(sha(b(self.body)).digest())) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
if 'oauth_consumer_key' not in self: | ||
self['oauth_consumer_key'] = consumer.key | ||
|
@@ -605,18 +657,20 @@ def _split_header(header): | |
# Split key-value. | ||
param_parts = param.split('=', 1) | ||
# Remove quotes and unescape the value. | ||
params[param_parts[0]] = urllib.unquote(param_parts[1].strip('\"')) | ||
params[param_parts[0]] = unquote(param_parts[1].strip('\"')) | ||
return params | ||
|
||
@staticmethod | ||
def _split_url_string(param_str): | ||
"""Turn URL string into parameters.""" | ||
parameters = parse_qs(param_str.encode('utf-8'), keep_blank_values=True) | ||
for k, v in parameters.iteritems(): | ||
if not PY3: | ||
param_str = param_str.encode('utf8') | ||
parameters = parse_qs(param_str, keep_blank_values=True) | ||
for k, v in iteritems(parameters): | ||
if len(v) == 1: | ||
parameters[k] = urllib.unquote(v[0]) | ||
parameters[k] = unquote(v[0]) | ||
else: | ||
parameters[k] = sorted([urllib.unquote(s) for s in v]) | ||
parameters[k] = sorted([unquote(s) for s in v]) | ||
return parameters | ||
|
||
|
||
|
@@ -668,12 +722,12 @@ def request(self, uri, method="GET", body='', headers=None, | |
|
||
req.sign_request(self.method, self.consumer, self.token) | ||
|
||
schema, rest = urllib.splittype(uri) | ||
schema, rest = splittype(uri) | ||
if rest.startswith('//'): | ||
hierpart = '//' | ||
else: | ||
hierpart = '' | ||
host, rest = urllib.splithost(rest) | ||
host, rest = splithost(rest) | ||
|
||
realm = schema + ':' + hierpart + host | ||
|
||
|
@@ -844,10 +898,10 @@ def sign(self, request, consumer, token): | |
"""Builds the base signature string.""" | ||
key, raw = self.signing_base(request, consumer, token) | ||
|
||
hashed = hmac.new(key, raw, sha) | ||
hashed = hmac.new(b(key), b(raw), sha) | ||
|
||
# Calculate the digest base 64. | ||
return binascii.b2a_base64(hashed.digest())[:-1] | ||
return s(binascii.b2a_base64(hashed.digest())[:-1]) | ||
|
||
|
||
class SignatureMethod_PLAINTEXT(SignatureMethod): | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
#!/usr/bin/env python | ||
from setuptools import setup | ||
import os, re | ||
import os, re, sys | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
from setuptools import setup, find_packages | ||
|
||
PKG='oauth2' | ||
VERSIONFILE = os.path.join('oauth2', '_version.py') | ||
|
@@ -15,7 +16,7 @@ | |
if mo: | ||
mverstr = mo.group(1) | ||
else: | ||
print "unable to find version in %s" % (VERSIONFILE,) | ||
sys.stdout.write("unable to find version in %s\n" % (VERSIONFILE,)) | ||
raise RuntimeError("if %s.py exists, it must be well-formed" % (VERSIONFILE,)) | ||
AVSRE = r"^auto_build_num *= *['\"]([^'\"]*)['\"]" | ||
mo = re.search(AVSRE, verstrline, re.M) | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.