Skip to content

Commit

Permalink
Support Python 3
Browse files Browse the repository at this point in the history
These are mostly unicode string related changes and a few syntax ones.

#55
  • Loading branch information
mikix committed Aug 31, 2012
1 parent dcbc028 commit 95c2463
Show file tree
Hide file tree
Showing 21 changed files with 791 additions and 718 deletions.
98 changes: 64 additions & 34 deletions oauthlib/common.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import absolute_import, unicode_literals

"""
oauthlib.common
Expand All @@ -11,35 +11,65 @@

import random
import re
import string
import sys
import time
import urllib
import urlparse

UNICODE_ASCII_CHARACTER_SET = (string.ascii_letters.decode('ascii') +
string.digits.decode('ascii'))

always_safe = (u'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
u'abcdefghijklmnopqrstuvwxyz'
u'0123456789' u'_.-')


def quote(s, safe=u'/'):
encoded = s.encode("utf-8")
quoted = urllib.quote(encoded, safe)
return quoted.decode("utf-8")
try:
from urllib import quote as _quote
from urllib import unquote as _unquote
from urllib import urlencode as _urlencode
except ImportError:
from urllib.parse import quote as _quote
from urllib.parse import unquote as _unquote
from urllib.parse import urlencode as _urlencode
try:
import urlparse
except ImportError:
import urllib.parse as urlparse

UNICODE_ASCII_CHARACTER_SET = ('abcdefghijklmnopqrstuvwxyz'
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'0123456789')

always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ'
'abcdefghijklmnopqrstuvwxyz'
'0123456789' '_.-')

PY3 = sys.version_info[0] == 3


if PY3:
unicode_type = str
bytes_type = bytes
else:
unicode_type = unicode
bytes_type = str

def quote(s, safe='/'):
# PY3 always returns unicode. PY2 may return either, depending on whether
# it had to modify the string.
s = _quote(s, safe)
if isinstance(s, bytes_type):
s = s.decode('utf-8')
return s


def unquote(s):
encoded = s.encode("utf-8")
unquoted = urllib.unquote(encoded)
return unquoted.decode("utf-8")
# PY3 always returns unicode. PY2 seems to always return what you give it,
# which differs from quote's behavior. Just to be safe, make sure it is
# unicode before we return.
s = _unquote(s)
if isinstance(s, bytes_type):
s = s.decode('utf-8')
return s


def urlencode(params):
utf8_params = encode_params_utf8(params)
urlencoded = urllib.urlencode(utf8_params)
return urlencoded.decode("utf-8")
urlencoded = _urlencode(utf8_params)
if isinstance(urlencoded, unicode_type): # PY3 returns unicode
return urlencoded
else:
return urlencoded.decode("utf-8")


def encode_params_utf8(params):
Expand All @@ -49,8 +79,8 @@ def encode_params_utf8(params):
encoded = []
for k, v in params:
encoded.append((
k.encode('utf-8') if isinstance(k, unicode) else k,
v.encode('utf-8') if isinstance(v, unicode) else v))
k.encode('utf-8') if isinstance(k, unicode_type) else k,
v.encode('utf-8') if isinstance(v, unicode_type) else v))
return encoded


Expand All @@ -61,12 +91,12 @@ def decode_params_utf8(params):
decoded = []
for k, v in params:
decoded.append((
k.decode('utf-8') if isinstance(k, str) else k,
v.decode('utf-8') if isinstance(v, str) else v))
k.decode('utf-8') if isinstance(k, bytes_type) else k,
v.decode('utf-8') if isinstance(v, bytes_type) else v))
return decoded


urlencoded = set(always_safe) | set(u'=&;%+~')
urlencoded = set(always_safe) | set('=&;%+~')


def urldecode(query):
Expand All @@ -86,11 +116,11 @@ def urldecode(query):
# All encoded values begin with % followed by two hex characters
# correct = %00, %A0, %0A, %FF
# invalid = %G0, %5H, %PO
invalid_hex = u'%[^0-9A-Fa-f]|%[0-9A-Fa-f][^0-9A-Fa-f]'
invalid_hex = '%[^0-9A-Fa-f]|%[0-9A-Fa-f][^0-9A-Fa-f]'
if len(re.findall(invalid_hex, query)):
raise ValueError('Invalid hex encoding in query string.')

query = query.decode('utf-8') if isinstance(query, str) else query
query = query.decode('utf-8') if isinstance(query, bytes_type) else query
# We want to allow queries such as "c2" whereas urlparse.parse_qsl
# with the strict_parsing flag will not.
params = urlparse.parse_qsl(query, keep_blank_values=True)
Expand All @@ -107,7 +137,7 @@ def extract_params(raw):
empty list of parameters. Any other input will result in a return
value of None.
"""
if isinstance(raw, basestring):
if isinstance(raw, bytes_type) or isinstance(raw, unicode_type):
try:
params = urldecode(raw)
except ValueError:
Expand Down Expand Up @@ -140,7 +170,7 @@ def generate_nonce():
.. _`section 3.2.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-3.2.1
.. _`section 3.3`: http://tools.ietf.org/html/rfc5849#section-3.3
"""
return unicode(unicode(random.getrandbits(64)) + generate_timestamp())
return unicode_type(unicode_type(random.getrandbits(64)) + generate_timestamp())


def generate_timestamp():
Expand All @@ -152,7 +182,7 @@ def generate_timestamp():
.. _`section 3.2.1`: http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-3.2.1
.. _`section 3.3`: http://tools.ietf.org/html/rfc5849#section-3.3
"""
return unicode(int(time.time()))
return unicode_type(int(time.time()))


def generate_token(length=30, chars=UNICODE_ASCII_CHARACTER_SET):
Expand All @@ -164,7 +194,7 @@ def generate_token(length=30, chars=UNICODE_ASCII_CHARACTER_SET):
why SystemRandom is used instead of the default random.choice method.
"""
rand = random.SystemRandom()
return u''.join(rand.choice(chars) for x in range(length))
return ''.join(rand.choice(chars) for x in range(length))


def add_params_to_qs(query, params):
Expand Down Expand Up @@ -214,7 +244,7 @@ class Request(object):
unmolested.
"""

def __init__(self, uri, http_method=u'GET', body=None, headers=None):
def __init__(self, uri, http_method='GET', body=None, headers=None):
self.uri = uri
self.http_method = http_method
self.headers = headers or {}
Expand Down
2 changes: 1 addition & 1 deletion oauthlib/oauth1/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import absolute_import, unicode_literals

"""
oauthlib.oauth1
Expand Down
75 changes: 39 additions & 36 deletions oauthlib/oauth1/rfc5849/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import absolute_import, unicode_literals

"""
oauthlib.oauth1.rfc5849
Expand All @@ -11,22 +11,25 @@

import logging
import time
import urlparse
try:
import urlparse
except ImportError:
import urllib.parse as urlparse

from oauthlib.common import Request, urlencode, generate_nonce
from oauthlib.common import generate_timestamp
from . import parameters, signature, utils

SIGNATURE_HMAC = u"HMAC-SHA1"
SIGNATURE_RSA = u"RSA-SHA1"
SIGNATURE_PLAINTEXT = u"PLAINTEXT"
SIGNATURE_HMAC = "HMAC-SHA1"
SIGNATURE_RSA = "RSA-SHA1"
SIGNATURE_PLAINTEXT = "PLAINTEXT"
SIGNATURE_METHODS = (SIGNATURE_HMAC, SIGNATURE_RSA, SIGNATURE_PLAINTEXT)

SIGNATURE_TYPE_AUTH_HEADER = u'AUTH_HEADER'
SIGNATURE_TYPE_QUERY = u'QUERY'
SIGNATURE_TYPE_BODY = u'BODY'
SIGNATURE_TYPE_AUTH_HEADER = 'AUTH_HEADER'
SIGNATURE_TYPE_QUERY = 'QUERY'
SIGNATURE_TYPE_BODY = 'BODY'

CONTENT_TYPE_FORM_URLENCODED = u'application/x-www-form-urlencoded'
CONTENT_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded'


class Client(object):
Expand Down Expand Up @@ -94,18 +97,18 @@ def get_oauth_params(self):
"""Get the basic OAuth parameters to be used in generating a signature.
"""
params = [
(u'oauth_nonce', generate_nonce()),
(u'oauth_timestamp', generate_timestamp()),
(u'oauth_version', u'1.0'),
(u'oauth_signature_method', self.signature_method),
(u'oauth_consumer_key', self.client_key),
('oauth_nonce', generate_nonce()),
('oauth_timestamp', generate_timestamp()),
('oauth_version', '1.0'),
('oauth_signature_method', self.signature_method),
('oauth_consumer_key', self.client_key),
]
if self.resource_owner_key:
params.append((u'oauth_token', self.resource_owner_key))
params.append(('oauth_token', self.resource_owner_key))
if self.callback_uri:
params.append((u'oauth_callback', self.callback_uri))
params.append(('oauth_callback', self.callback_uri))
if self.verifier:
params.append((u'oauth_verifier', self.verifier))
params.append(('oauth_verifier', self.verifier))

return params

Expand Down Expand Up @@ -135,15 +138,15 @@ def _render(self, request, formencode=False):
body = parameters.prepare_form_encoded_body(request.oauth_params, request.decoded_body)
if formencode:
body = urlencode(body)
headers['Content-Type'] = u'application/x-www-form-urlencoded'
headers['Content-Type'] = 'application/x-www-form-urlencoded'
elif self.signature_type == SIGNATURE_TYPE_QUERY:
uri = parameters.prepare_request_uri_query(request.oauth_params, request.uri)
else:
raise ValueError('Unknown signature type specified.')

return uri, headers, body

def sign(self, uri, http_method=u'GET', body=None, headers=None):
def sign(self, uri, http_method='GET', body=None, headers=None):
"""Sign a request
Signs an HTTP request with the specified parts.
Expand Down Expand Up @@ -208,7 +211,7 @@ def sign(self, uri, http_method=u'GET', body=None, headers=None):
request.oauth_params = self.get_oauth_params()

# generate the signature
request.oauth_params.append((u'oauth_signature', self.get_oauth_signature(request)))
request.oauth_params.append(('oauth_signature', self.get_oauth_signature(request)))

# render the signed request and return it
return self._render(request, formencode=True)
Expand Down Expand Up @@ -465,14 +468,14 @@ def get_signature_type_and_params(self, request):
params.extend(header_params)
params.extend(body_params)
params.extend(query_params)
signature_types_with_oauth_params = filter(lambda s: s[2], (
signature_types_with_oauth_params = list(filter(lambda s: s[2], (
(SIGNATURE_TYPE_AUTH_HEADER, params,
utils.filter_oauth_params(header_params)),
(SIGNATURE_TYPE_BODY, params,
utils.filter_oauth_params(body_params)),
(SIGNATURE_TYPE_QUERY, params,
utils.filter_oauth_params(query_params))
))
)))

if len(signature_types_with_oauth_params) > 1:
raise ValueError('oauth_ params must come from only 1 signature type but were found in %s' % ', '.join(
Expand Down Expand Up @@ -610,7 +613,7 @@ def validate_verifier(self, client_key, request_token, verifier):
"""
raise NotImplementedError("Subclasses must implement this function.")

def verify_request(self, uri, http_method=u'GET', body=None,
def verify_request(self, uri, http_method='GET', body=None,
headers=None, require_resource_owner=True, require_verifier=False,
require_realm=False, required_realm=None):
"""Verifies a request ensuring that the following is true:
Expand Down Expand Up @@ -644,11 +647,11 @@ def verify_request(self, uri, http_method=u'GET', body=None,
"""
# Only include body data from x-www-form-urlencoded requests
headers = headers or {}
if (u"Content-Type" in headers and
headers[u"Content-Type"] == CONTENT_TYPE_FORM_URLENCODED):
if ("Content-Type" in headers and
headers["Content-Type"] == CONTENT_TYPE_FORM_URLENCODED):
request = Request(uri, http_method, body, headers)
else:
request = Request(uri, http_method, u'', headers)
request = Request(uri, http_method, '', headers)

if self.enforce_ssl and not request.uri.lower().startswith("https://"):
raise ValueError("Insecure transport, only HTTPS is allowed.")
Expand All @@ -661,15 +664,15 @@ def verify_request(self, uri, http_method=u'GET', body=None,
raise ValueError("Duplicate OAuth entries.")

oauth_params = dict(oauth_params)
request_signature = oauth_params.get(u'oauth_signature')
client_key = oauth_params.get(u'oauth_consumer_key')
resource_owner_key = oauth_params.get(u'oauth_token')
nonce = oauth_params.get(u'oauth_nonce')
timestamp = oauth_params.get(u'oauth_timestamp')
callback_uri = oauth_params.get(u'oauth_callback')
verifier = oauth_params.get(u'oauth_verifier')
signature_method = oauth_params.get(u'oauth_signature_method')
realm = dict(params).get(u'realm')
request_signature = oauth_params.get('oauth_signature')
client_key = oauth_params.get('oauth_consumer_key')
resource_owner_key = oauth_params.get('oauth_token')
nonce = oauth_params.get('oauth_nonce')
timestamp = oauth_params.get('oauth_timestamp')
callback_uri = oauth_params.get('oauth_callback')
verifier = oauth_params.get('oauth_verifier')
signature_method = oauth_params.get('oauth_signature_method')
realm = dict(params).get('realm')

# The server SHOULD return a 400 (Bad Request) status code when
# receiving a request with missing parameters.
Expand All @@ -691,7 +694,7 @@ def verify_request(self, uri, http_method=u'GET', body=None,
# Servers receiving an authenticated request MUST validate it by:
# If the "oauth_version" parameter is present, ensuring its value is
# "1.0".
if u'oauth_version' in oauth_params and oauth_params[u'oauth_version'] != u'1.0':
if 'oauth_version' in oauth_params and oauth_params['oauth_version'] != '1.0':
raise ValueError("Invalid OAuth version.")

# The timestamp value MUST be a positive integer. Unless otherwise
Expand Down

0 comments on commit 95c2463

Please sign in to comment.