Skip to content

Commit

Permalink
Merge pull request #700 from hugovk/rm-2.7
Browse files Browse the repository at this point in the history
Drop support for Python 2.7
  • Loading branch information
JonathanHuot committed Aug 30, 2019
2 parents 1f3fc4b + 11bf0ec commit ca57b0b
Show file tree
Hide file tree
Showing 119 changed files with 212 additions and 530 deletions.
3 changes: 0 additions & 3 deletions .travis.yml
@@ -1,12 +1,9 @@
language: python
python: 3.7
dist: xenial
sudo: false
cache: pip
matrix:
include:
- python: 2.7
env: TOXENV=py27
- python: 3.5
env: TOXENV=py35
- python: 3.6
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Expand Up @@ -2,7 +2,7 @@ OAuthLib - Python Framework for OAuth1 & OAuth2
===============================================

*A generic, spec-compliant, thorough implementation of the OAuth request-signing
logic for Python 2.7 and 3.5+.*
logic for Python 3.5+.
.. image:: https://travis-ci.org/oauthlib/oauthlib.svg?branch=master
:target: https://travis-ci.org/oauthlib/oauthlib
Expand Down
2 changes: 1 addition & 1 deletion bandit.json
Expand Up @@ -1133,7 +1133,7 @@
"test_name": "hardcoded_password_funcarg"
},
{
"code": "164 \n165 def prepare_token_revocation_request(url, token, token_type_hint=\"access_token\",\n166 callback=None, body='', **kwargs):\n167 \"\"\"Prepare a token revocation request.\n168 \n169 The client constructs the request by including the following parameters\n170 using the \"application/x-www-form-urlencoded\" format in the HTTP request\n171 entity-body:\n172 \n173 :param token: REQUIRED. The token that the client wants to get revoked.\n174 \n175 :param token_type_hint: OPTIONAL. A hint about the type of the token\n176 submitted for revocation. Clients MAY pass this\n177 parameter in order to help the authorization server\n178 to optimize the token lookup. If the server is\n179 unable to locate the token using the given hint, it\n180 MUST extend its search across all of its supported\n181 token types. An authorization server MAY ignore\n182 this parameter, particularly if it is able to detect\n183 the token type automatically.\n184 \n185 This specification defines two values for `token_type_hint`:\n186 \n187 * access_token: An access token as defined in [RFC6749],\n188 `Section 1.4`_\n189 \n190 * refresh_token: A refresh token as defined in [RFC6749],\n191 `Section 1.5`_\n192 \n193 Specific implementations, profiles, and extensions of this\n194 specification MAY define other values for this parameter using the\n195 registry defined in `Section 4.1.2`_.\n196 \n197 .. _`Section 1.4`: https://tools.ietf.org/html/rfc6749#section-1.4\n198 .. _`Section 1.5`: https://tools.ietf.org/html/rfc6749#section-1.5\n199 .. _`Section 4.1.2`: https://tools.ietf.org/html/rfc7009#section-4.1.2\n200 \n201 \"\"\"\n202 if not is_secure_transport(url):\n203 raise InsecureTransportError()\n204 \n205 params = [('token', token)]\n206 \n207 if token_type_hint:\n208 params.append(('token_type_hint', token_type_hint))\n209 \n210 for k in kwargs:\n211 if kwargs[k]:\n212 params.append((unicode_type(k), kwargs[k]))\n213 \n214 headers = {'Content-Type': 'application/x-www-form-urlencoded'}\n215 \n216 if callback:\n217 params.append(('callback', callback))\n218 return add_params_to_uri(url, params), headers, body\n219 else:\n220 return url, headers, add_params_to_qs(body, params)\n221 \n222 \n223 def parse_authorization_code_response(uri, state=None):\n",
"code": "164 \n165 def prepare_token_revocation_request(url, token, token_type_hint=\"access_token\",\n166 callback=None, body='', **kwargs):\n167 \"\"\"Prepare a token revocation request.\n168 \n169 The client constructs the request by including the following parameters\n170 using the \"application/x-www-form-urlencoded\" format in the HTTP request\n171 entity-body:\n172 \n173 :param token: REQUIRED. The token that the client wants to get revoked.\n174 \n175 :param token_type_hint: OPTIONAL. A hint about the type of the token\n176 submitted for revocation. Clients MAY pass this\n177 parameter in order to help the authorization server\n178 to optimize the token lookup. If the server is\n179 unable to locate the token using the given hint, it\n180 MUST extend its search across all of its supported\n181 token types. An authorization server MAY ignore\n182 this parameter, particularly if it is able to detect\n183 the token type automatically.\n184 \n185 This specification defines two values for `token_type_hint`:\n186 \n187 * access_token: An access token as defined in [RFC6749],\n188 `Section 1.4`_\n189 \n190 * refresh_token: A refresh token as defined in [RFC6749],\n191 `Section 1.5`_\n192 \n193 Specific implementations, profiles, and extensions of this\n194 specification MAY define other values for this parameter using the\n195 registry defined in `Section 4.1.2`_.\n196 \n197 .. _`Section 1.4`: https://tools.ietf.org/html/rfc6749#section-1.4\n198 .. _`Section 1.5`: https://tools.ietf.org/html/rfc6749#section-1.5\n199 .. _`Section 4.1.2`: https://tools.ietf.org/html/rfc7009#section-4.1.2\n200 \n201 \"\"\"\n202 if not is_secure_transport(url):\n203 raise InsecureTransportError()\n204 \n205 params = [('token', token)]\n206 \n207 if token_type_hint:\n208 params.append(('token_type_hint', token_type_hint))\n209 \n210 for k in kwargs:\n211 if kwargs[k]:\n212 params.append((str(k), kwargs[k]))\n213 \n214 headers = {'Content-Type': 'application/x-www-form-urlencoded'}\n215 \n216 if callback:\n217 params.append(('callback', callback))\n218 return add_params_to_uri(url, params), headers, body\n219 else:\n220 return url, headers, add_params_to_qs(body, params)\n221 \n222 \n223 def parse_authorization_code_response(uri, state=None):\n",
"filename": "oauthlib/oauth2/rfc6749/parameters.py",
"issue_confidence": "MEDIUM",
"issue_severity": "LOW",
Expand Down
16 changes: 8 additions & 8 deletions docs/conf.py
Expand Up @@ -45,8 +45,8 @@
master_doc = 'index'

# General information about the project.
project = u'OAuthLib'
copyright = u'2019, The OAuthlib Community'
project = 'OAuthLib'
copyright = '2019, The OAuthlib Community'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
Expand Down Expand Up @@ -190,8 +190,8 @@
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'OAuthLib.tex', u'OAuthLib Documentation',
u'The OAuhthlib Community', 'manual'),
('index', 'OAuthLib.tex', 'OAuthLib Documentation',
'The OAuhthlib Community', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
Expand Down Expand Up @@ -220,8 +220,8 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'oauthlib', u'OAuthLib Documentation',
[u'The OAuthlib Community'], 1)
('index', 'oauthlib', 'OAuthLib Documentation',
['The OAuthlib Community'], 1)
]

# If true, show URL addresses after external links.
Expand All @@ -234,8 +234,8 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'OAuthLib', u'OAuthLib Documentation',
u'The OAuthlib Community', 'OAuthLib', 'One line description of project.',
('index', 'OAuthLib', 'OAuthLib Documentation',
'The OAuthlib Community', 'OAuthLib', 'One line description of project.',
'Miscellaneous'),
]

Expand Down
7 changes: 3 additions & 4 deletions docs/contributing.rst
Expand Up @@ -144,15 +144,15 @@ the project root via:

.. sourcecode:: bash

$ py.test
$ pytest

The first thing the core committers will do is run this command. Any pull
request that fails this test suite will be **rejected**.

Testing multiple versions of Python
-----------------------------------

OAuthLib supports Python 2.7, 3.5, 3.6, 3.7 and PyPy 2.7 & PyPy 3. Testing
OAuthLib supports Python 3.5, 3.6, 3.7 and PyPy 2.7 & PyPy 3. Testing
all versions conveniently can be done using `Tox`_.

.. sourcecode:: bash
Expand All @@ -167,7 +167,6 @@ The versions beloew may not be up to date.

.. sourcecode:: bash

$ pyenv install 2.7.16
$ pyenv install 3.5.7
$ pyenv install 3.6.9
$ pyenv install 3.7.4
Expand Down Expand Up @@ -304,7 +303,7 @@ First we pull the code into a local branch::

Then we run the tests::

py.test
pytest

We finish with a non-fastforward merge (to preserve the branch history) and push
to GitHub::
Expand Down
76 changes: 21 additions & 55 deletions oauthlib/common.py
Expand Up @@ -6,34 +6,23 @@
This module provides data structures and utilities common
to all implementations of OAuth.
"""
from __future__ import absolute_import, unicode_literals

import collections
import datetime
import logging
import re
import sys
import time
import urllib.parse as urlparse
from . import get_debug
from urllib.parse import quote as _quote
from urllib.parse import unquote as _unquote
from urllib.parse import urlencode as _urlencode

try:
from secrets import randbits
from secrets import SystemRandom
except ImportError:
from random import getrandbits as randbits
from random import SystemRandom
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'
Expand All @@ -51,17 +40,10 @@

log = logging.getLogger('oauthlib')

PY3 = sys.version_info[0] == 3

if PY3:
unicode_type = str
else:
unicode_type = unicode


# 'safe' must be bytes (Python 2.6 requires bytes, other versions allow either)
def quote(s, safe=b'/'):
s = s.encode('utf-8') if isinstance(s, unicode_type) else s
s = s.encode('utf-8') if isinstance(s, str) else s
s = _quote(s, safe)
# PY3 always returns unicode. PY2 may return either, depending on whether
# it had to modify the string.
Expand All @@ -83,7 +65,7 @@ def unquote(s):
def urlencode(params):
utf8_params = encode_params_utf8(params)
urlencoded = _urlencode(utf8_params)
if isinstance(urlencoded, unicode_type): # PY3 returns unicode
if isinstance(urlencoded, str):
return urlencoded
else:
return urlencoded.decode("utf-8")
Expand All @@ -96,8 +78,8 @@ def encode_params_utf8(params):
encoded = []
for k, v in params:
encoded.append((
k.encode('utf-8') if isinstance(k, unicode_type) else k,
v.encode('utf-8') if isinstance(v, unicode_type) else v))
k.encode('utf-8') if isinstance(k, str) else k,
v.encode('utf-8') if isinstance(v, str) else v))
return encoded


Expand Down Expand Up @@ -141,22 +123,6 @@ def urldecode(query):
if INVALID_HEX_PATTERN.search(query):
raise ValueError('Invalid hex encoding in query string.')

# We encode to utf-8 prior to parsing because parse_qsl behaves
# differently on unicode input in python 2 and 3.
# Python 2.7
# >>> urlparse.parse_qsl(u'%E5%95%A6%E5%95%A6')
# u'\xe5\x95\xa6\xe5\x95\xa6'
# Python 2.7, non unicode input gives the same
# >>> urlparse.parse_qsl('%E5%95%A6%E5%95%A6')
# '\xe5\x95\xa6\xe5\x95\xa6'
# but now we can decode it to unicode
# >>> urlparse.parse_qsl('%E5%95%A6%E5%95%A6').decode('utf-8')
# u'\u5566\u5566'
# Python 3.3 however
# >>> urllib.parse.parse_qsl(u'%E5%95%A6%E5%95%A6')
# u'\u5566\u5566'
query = query.encode(
'utf-8') if not PY3 and isinstance(query, unicode_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 @@ -173,7 +139,7 @@ def extract_params(raw):
empty list of parameters. Any other input will result in a return
value of None.
"""
if isinstance(raw, (bytes, unicode_type)):
if isinstance(raw, (bytes, str)):
try:
params = urldecode(raw)
except ValueError:
Expand Down Expand Up @@ -206,7 +172,7 @@ def generate_nonce():
.. _`section 3.2.1`: https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01#section-3.2.1
.. _`section 3.3`: https://tools.ietf.org/html/rfc5849#section-3.3
"""
return unicode_type(unicode_type(randbits(64)) + generate_timestamp())
return str(str(randbits(64)) + generate_timestamp())


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


def generate_token(length=30, chars=UNICODE_ASCII_CHARACTER_SET):
Expand Down Expand Up @@ -305,11 +271,11 @@ def safe_string_equals(a, b):

def to_unicode(data, encoding='UTF-8'):
"""Convert a number of different types of objects to unicode."""
if isinstance(data, unicode_type):
if isinstance(data, str):
return data

if isinstance(data, bytes):
return unicode_type(data, encoding=encoding)
return str(data, encoding=encoding)

if hasattr(data, '__iter__'):
try:
Expand All @@ -323,7 +289,7 @@ def to_unicode(data, encoding='UTF-8'):
# We support 2.6 which lacks dict comprehensions
if hasattr(data, 'items'):
data = data.items()
return dict(((to_unicode(k, encoding), to_unicode(v, encoding)) for k, v in data))
return {to_unicode(k, encoding): to_unicode(v, encoding) for k, v in data}

return data

Expand All @@ -335,7 +301,7 @@ class CaseInsensitiveDict(dict):
proxy = {}

def __init__(self, data):
self.proxy = dict((k.lower(), k) for k in data)
self.proxy = {k.lower(): k for k in data}
for k in data:
self[k] = data[k]

Expand All @@ -344,27 +310,27 @@ def __contains__(self, k):

def __delitem__(self, k):
key = self.proxy[k.lower()]
super(CaseInsensitiveDict, self).__delitem__(key)
super().__delitem__(key)
del self.proxy[k.lower()]

def __getitem__(self, k):
key = self.proxy[k.lower()]
return super(CaseInsensitiveDict, self).__getitem__(key)
return super().__getitem__(key)

def get(self, k, default=None):
return self[k] if k in self else default

def __setitem__(self, k, v):
super(CaseInsensitiveDict, self).__setitem__(k, v)
super().__setitem__(k, v)
self.proxy[k.lower()] = k

def update(self, *args, **kwargs):
super(CaseInsensitiveDict, self).update(*args, **kwargs)
super().update(*args, **kwargs)
for k in dict(*args, **kwargs):
self.proxy[k.lower()] = k


class Request(object):
class Request:

"""A malleable representation of a signable HTTP request.
Expand Down Expand Up @@ -444,7 +410,7 @@ def __repr__(self):
body = SANITIZE_PATTERN.sub('\1<SANITIZED>', str(body))
if 'Authorization' in headers:
headers['Authorization'] = '<SANITIZED>'
return '<oauthlib.Request url="%s", http_method="%s", headers="%s", body="%s">' % (
return '<oauthlib.Request url="{}", http_method="{}", headers="{}", body="{}">'.format(
self.uri, self.http_method, headers, body)

@property
Expand Down
2 changes: 0 additions & 2 deletions oauthlib/oauth1/__init__.py
Expand Up @@ -6,8 +6,6 @@
This module is a wrapper for the most recent implementation of OAuth 1.0 Client
and Server classes.
"""
from __future__ import absolute_import, unicode_literals

from .rfc5849 import Client
from .rfc5849 import SIGNATURE_HMAC, SIGNATURE_HMAC_SHA1, SIGNATURE_HMAC_SHA256, SIGNATURE_RSA, SIGNATURE_PLAINTEXT
from .rfc5849 import SIGNATURE_TYPE_AUTH_HEADER, SIGNATURE_TYPE_QUERY
Expand Down

0 comments on commit ca57b0b

Please sign in to comment.