Skip to content
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

fix!: drop support for Python 2.7 #778

Merged
merged 21 commits into from Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
903dc86
fix!: drop Python 2.7 support
tseaver Jun 16, 2021
fa03692
chore: drop use of 'six' wrapper library
tseaver Jun 16, 2021
ea0468d
tests: use editable install for tests
tseaver Jun 16, 2021
00c684d
chore: drop 'u"' prefixes
tseaver Jun 16, 2021
4dc4b09
tests: drop running systests w/ Python 2.7
tseaver Jun 16, 2021
9deeacc
tests: fix imports from 'http.server'
tseaver Jun 16, 2021
8759b96
tests: drop systest for app_engine 'classic' mode (Python 2.7)
tseaver Jun 16, 2021
1682dde
docs: document removed versions of Python
tseaver Jun 16, 2021
5f871ed
Merge branch 'master' into 777-drop-python-2.7
tseaver Jun 18, 2021
1ff605d
docs: typo
tseaver Jun 18, 2021
19aa0a8
Merge branch 'master' into 777-drop-python-2.7
tseaver Jun 24, 2021
076959d
Merge branch 'master' into 777-drop-python-2.7
tseaver Jul 8, 2021
aa996c2
Merge branch 'master' into 777-drop-python-2.7
tseaver Jul 14, 2021
0469a2b
Merge branch '777-drop-python-2.7' of github.com:googleapis/google-au…
tseaver Jul 14, 2021
b8951ee
Merge branch 'master' into 777-drop-python-2.7
tseaver Jul 14, 2021
4a6c5c2
Merge branch 'master' into 777-drop-python-2.7
tseaver Jul 23, 2021
922bc4e
chore: adjust last 2.7 release number
tseaver Jul 23, 2021
8fece64
Merge branch 'master' into 777-drop-python-2.7
tseaver Jul 27, 2021
a203c5e
Merge branch 'master' into 777-drop-python-2.7
tseaver Aug 1, 2021
a3054a4
chore: remove Python 2.7-compat import fallback
tseaver Aug 1, 2021
35f4546
chore: allow release-bot to set new release number
tseaver Aug 3, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions CONTRIBUTING.rst
Expand Up @@ -18,8 +18,8 @@ A few notes on making changes to ``google-auth-library-python``.
documentation (in ``docs/``). You can re-generate the reference documentation
using ``nox -s docgen``.

- The change must work fully on the following CPython versions: 2.7,
3.5, 3.6, 3.7, 3.8 across macOS, Linux, and Windows.
- The change must work fully on the following CPython versions:
3.6, 3.7, 3.8, 3.9 across macOS, Linux, and Windows.

- The codebase *must* have 100% test statement coverage after each commit.
You can test coverage via ``nox -e cover``.
Expand Down
12 changes: 8 additions & 4 deletions README.rst
Expand Up @@ -24,11 +24,15 @@ For more information on setting up your Python development environment, please r

Supported Python Versions
^^^^^^^^^^^^^^^^^^^^^^^^^
Python >= 3.5
Python >= 3.6

Deprecated Python Versions
^^^^^^^^^^^^^^^^^^^^^^^^^^
Python == 2.7. Python 2.7 support will be removed on January 1, 2020.
Unsupported Python Versions
^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Python == 2.7: The last version of this library with support for Python 2.7
was `google.auth == 1.34.0`.

- Python 3.5: The last version of this library with support for Python 3.5
was `google.auth == 1.23.0`.

Documentation
-------------
Expand Down
4 changes: 1 addition & 3 deletions google/auth/_cloud_sdk.py
Expand Up @@ -18,8 +18,6 @@
import os
import subprocess

import six

from google.auth import environment_vars
from google.auth import exceptions

Expand Down Expand Up @@ -156,4 +154,4 @@ def get_auth_access_token(account=None):
new_exc = exceptions.UserAccessTokenError(
"Failed to obtain access token", caught_exc
)
six.raise_from(new_exc, caught_exc)
raise new_exc from caught_exc
11 changes: 3 additions & 8 deletions google/auth/_credentials_async.py
Expand Up @@ -18,13 +18,10 @@
import abc
import inspect

import six

from google.auth import credentials


@six.add_metaclass(abc.ABCMeta)
class Credentials(credentials.Credentials):
class Credentials(credentials.Credentials, metaclass=abc.ABCMeta):
"""Async inherited credentials class from google.auth.credentials.
The added functionality is the before_request call which requires
async/await syntax.
Expand Down Expand Up @@ -84,8 +81,7 @@ class AnonymousCredentials(credentials.AnonymousCredentials, Credentials):
"""


@six.add_metaclass(abc.ABCMeta)
class ReadOnlyScoped(credentials.ReadOnlyScoped):
class ReadOnlyScoped(credentials.ReadOnlyScoped, metaclass=abc.ABCMeta):
"""Interface for credentials whose scopes can be queried.

OAuth 2.0-based credentials allow limiting access using scopes as described
Expand Down Expand Up @@ -171,6 +167,5 @@ def with_scopes_if_required(credentials, scopes):
return credentials


@six.add_metaclass(abc.ABCMeta)
class Signing(credentials.Signing):
class Signing(credentials.Signing, metaclass=abc.ABCMeta):
"""Interface for credentials that can cryptographically sign messages."""
8 changes: 3 additions & 5 deletions google/auth/_default.py
Expand Up @@ -23,8 +23,6 @@
import os
import warnings

import six

from google.auth import environment_vars
from google.auth import exceptions
import google.auth.transport._http_client
Expand Down Expand Up @@ -115,7 +113,7 @@ def load_credentials_from_file(
new_exc = exceptions.DefaultCredentialsError(
"File {} is not a valid json file.".format(filename), caught_exc
)
six.raise_from(new_exc, caught_exc)
raise new_exc from caught_exc

# The type key should indicate that the file is either a service account
# credentials file or an authorized user credentials file.
Expand All @@ -131,7 +129,7 @@ def load_credentials_from_file(
except ValueError as caught_exc:
msg = "Failed to load authorized user credentials from {}".format(filename)
new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
six.raise_from(new_exc, caught_exc)
raise new_exc from caught_exc
if quota_project_id:
credentials = credentials.with_quota_project(quota_project_id)
if not credentials.quota_project_id:
Expand All @@ -148,7 +146,7 @@ def load_credentials_from_file(
except ValueError as caught_exc:
msg = "Failed to load service account credentials from {}".format(filename)
new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
six.raise_from(new_exc, caught_exc)
raise new_exc from caught_exc
if quota_project_id:
credentials = credentials.with_quota_project(quota_project_id)
return credentials, info.get("project_id")
Expand Down
8 changes: 3 additions & 5 deletions google/auth/_default_async.py
Expand Up @@ -21,8 +21,6 @@
import json
import os

import six

from google.auth import _default
from google.auth import environment_vars
from google.auth import exceptions
Expand Down Expand Up @@ -63,7 +61,7 @@ def load_credentials_from_file(filename, scopes=None, quota_project_id=None):
new_exc = exceptions.DefaultCredentialsError(
"File {} is not a valid json file.".format(filename), caught_exc
)
six.raise_from(new_exc, caught_exc)
raise new_exc from caught_exc

# The type key should indicate that the file is either a service account
# credentials file or an authorized user credentials file.
Expand All @@ -79,7 +77,7 @@ def load_credentials_from_file(filename, scopes=None, quota_project_id=None):
except ValueError as caught_exc:
msg = "Failed to load authorized user credentials from {}".format(filename)
new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
six.raise_from(new_exc, caught_exc)
raise new_exc from caught_exc
if not credentials.quota_project_id:
_default._warn_about_problematic_credentials(credentials)
return credentials, None
Expand All @@ -94,7 +92,7 @@ def load_credentials_from_file(filename, scopes=None, quota_project_id=None):
except ValueError as caught_exc:
msg = "Failed to load service account credentials from {}".format(filename)
new_exc = exceptions.DefaultCredentialsError(msg, caught_exc)
six.raise_from(new_exc, caught_exc)
raise new_exc from caught_exc
return credentials, info.get("project_id")

else:
Expand Down
17 changes: 6 additions & 11 deletions google/auth/_helpers.py
Expand Up @@ -17,9 +17,7 @@
import base64
import calendar
import datetime

import six
from six.moves import urllib
import urllib


CLOCK_SKEW_SECS = 10 # 10 seconds
Expand Down Expand Up @@ -84,9 +82,6 @@ def datetime_to_secs(value):
def to_bytes(value, encoding="utf-8"):
"""Converts a string value to bytes, if necessary.

Unfortunately, ``six.b`` is insufficient for this task since in
Python 2 because it does not modify ``unicode`` objects.

Args:
value (Union[str, bytes]): The value to be converted.
encoding (str): The encoding to use to convert unicode to bytes.
Expand All @@ -99,8 +94,8 @@ def to_bytes(value, encoding="utf-8"):
Raises:
ValueError: If the value could not be converted to bytes.
"""
result = value.encode(encoding) if isinstance(value, six.text_type) else value
if isinstance(result, six.binary_type):
result = value.encode(encoding) if isinstance(value, str) else value
if isinstance(result, bytes):
return result
else:
raise ValueError("{0!r} could not be converted to bytes".format(value))
Expand All @@ -119,8 +114,8 @@ def from_bytes(value):
Raises:
ValueError: If the value could not be converted to unicode.
"""
result = value.decode("utf-8") if isinstance(value, six.binary_type) else value
if isinstance(result, six.text_type):
result = value.decode("utf-8") if isinstance(value, bytes) else value
if isinstance(result, str):
return result
else:
raise ValueError("{0!r} could not be converted to unicode".format(value))
Expand Down Expand Up @@ -162,7 +157,7 @@ def update_query(url, params, remove=None):
query_params.update(params)
# Remove any values specified in remove.
query_params = {
key: value for key, value in six.iteritems(query_params) if key not in remove
key: value for key, value in query_params.items() if key not in remove
}
# Re-encoded the query string.
new_query = urllib.parse.urlencode(query_params, doseq=True)
Expand Down
6 changes: 2 additions & 4 deletions google/auth/_oauth2client.py
Expand Up @@ -21,8 +21,6 @@

from __future__ import absolute_import

import six

from google.auth import _helpers
import google.auth.app_engine
import google.auth.compute_engine
Expand All @@ -34,7 +32,7 @@
import oauth2client.contrib.gce
import oauth2client.service_account
except ImportError as caught_exc:
six.raise_from(ImportError("oauth2client is not installed."), caught_exc)
raise ImportError("oauth2client is not installed.") from caught_exc

try:
import oauth2client.contrib.appengine # pytype: disable=import-error
Expand Down Expand Up @@ -166,4 +164,4 @@ def convert(credentials):
return _CLASS_CONVERSION_MAP[credentials_class](credentials)
except KeyError as caught_exc:
new_exc = ValueError(_CONVERT_ERROR_TMPL.format(credentials_class))
six.raise_from(new_exc, caught_exc)
raise new_exc from caught_exc
4 changes: 1 addition & 3 deletions google/auth/_service_account_info.py
Expand Up @@ -17,8 +17,6 @@
import io
import json

import six

from google.auth import crypt


Expand All @@ -43,7 +41,7 @@ def from_dict(data, require=None):
"""
keys_needed = set(require if require is not None else [])

missing = keys_needed.difference(six.iterkeys(data))
missing = keys_needed.difference(data)

if missing:
raise ValueError(
Expand Down
9 changes: 4 additions & 5 deletions google/auth/aws.py
Expand Up @@ -39,13 +39,12 @@

import hashlib
import hmac
import http.client
import io
import json
import os
import re

from six.moves import http_client
from six.moves import urllib
import urllib

from google.auth import _helpers
from google.auth import environment_vars
Expand Down Expand Up @@ -627,7 +626,7 @@ def _get_metadata_security_credentials(self, request, role_name):
else response.data
)

if response.status != http_client.OK:
if response.status != http.client.OK:
raise exceptions.RefreshError(
"Unable to retrieve AWS security credentials", response_body
)
Expand Down Expand Up @@ -666,7 +665,7 @@ def _get_metadata_role_name(self, request):
else response.data
)

if response.status != http_client.OK:
if response.status != http.client.OK:
raise exceptions.RefreshError(
"Unable to retrieve AWS role name", response_body
)
Expand Down
12 changes: 5 additions & 7 deletions google/auth/compute_engine/_metadata.py
Expand Up @@ -18,13 +18,11 @@
"""

import datetime
import http.client
import json
import logging
import os

import six
from six.moves import http_client
from six.moves.urllib import parse as urlparse
from urllib import parse as urlparse

from google.auth import _helpers
from google.auth import environment_vars
Expand Down Expand Up @@ -91,7 +89,7 @@ def ping(request, timeout=_METADATA_DEFAULT_TIMEOUT, retry_count=3):

metadata_flavor = response.headers.get(_METADATA_FLAVOR_HEADER)
return (
response.status == http_client.OK
response.status == http.client.OK
and metadata_flavor == _METADATA_FLAVOR_VALUE
)

Expand Down Expand Up @@ -165,7 +163,7 @@ def get(
"metadata service. Compute Engine Metadata server unavailable".format(url)
)

if response.status == http_client.OK:
if response.status == http.client.OK:
content = _helpers.from_bytes(response.data)
if response.headers["content-type"] == "application/json":
try:
Expand All @@ -175,7 +173,7 @@ def get(
"Received invalid JSON from the Google Compute Engine"
"metadata service: {:.20}".format(content)
)
six.raise_from(new_exc, caught_exc)
raise new_exc from caught_exc
else:
return content
else:
Expand Down
8 changes: 3 additions & 5 deletions google/auth/compute_engine/credentials.py
Expand Up @@ -21,8 +21,6 @@

import datetime

import six

from google.auth import _helpers
from google.auth import credentials
from google.auth import exceptions
Expand All @@ -38,7 +36,7 @@ class Credentials(credentials.Scoped, credentials.CredentialsWithQuotaProject):
These credentials use the Google Compute Engine metadata server to obtain
OAuth 2.0 access tokens associated with the instance's service account,
and are also used for Cloud Run, Flex and App Engine (except for the Python
2.7 runtime).
2.7 runtime, which is supported only on older versions of this library).

For more information about Compute Engine authentication, including how
to configure scopes, see the `Compute Engine authentication
Expand Down Expand Up @@ -114,7 +112,7 @@ def refresh(self, request):
)
except exceptions.TransportError as caught_exc:
new_exc = exceptions.RefreshError(caught_exc)
six.raise_from(new_exc, caught_exc)
raise new_exc from caught_exc

@property
def service_account_email(self):
Expand Down Expand Up @@ -352,7 +350,7 @@ def _call_metadata_identity_endpoint(self, request):
id_token = _metadata.get(request, path, params=params)
except exceptions.TransportError as caught_exc:
new_exc = exceptions.RefreshError(caught_exc)
six.raise_from(new_exc, caught_exc)
raise new_exc from caught_exc

_, payload, _, _ = jwt._unverified_decode(id_token)
return id_token, datetime.datetime.fromtimestamp(payload["exp"])
Expand Down
11 changes: 3 additions & 8 deletions google/auth/credentials.py
Expand Up @@ -17,13 +17,10 @@

import abc

import six

from google.auth import _helpers


@six.add_metaclass(abc.ABCMeta)
class Credentials(object):
class Credentials(object, metaclass=abc.ABCMeta):
"""Base class for all credentials.

All credentials have a :attr:`token` that is used for authentication and
Expand Down Expand Up @@ -187,8 +184,7 @@ def before_request(self, request, method, url, headers):
"""Anonymous credentials do nothing to the request."""


@six.add_metaclass(abc.ABCMeta)
class ReadOnlyScoped(object):
class ReadOnlyScoped(object, metaclass=abc.ABCMeta):
"""Interface for credentials whose scopes can be queried.

OAuth 2.0-based credentials allow limiting access using scopes as described
Expand Down Expand Up @@ -329,8 +325,7 @@ def with_scopes_if_required(credentials, scopes, default_scopes=None):
return credentials


@six.add_metaclass(abc.ABCMeta)
class Signing(object):
class Signing(object, metaclass=abc.ABCMeta):
"""Interface for credentials that can cryptographically sign messages."""

@abc.abstractmethod
Expand Down