Skip to content

Commit

Permalink
fix!: drop support for Python 2.7 (#778)
Browse files Browse the repository at this point in the history
Drop use of 'six' wrapper library.

Drop 'u"' prefixes.

Drop support for app_engine 'classic' mode (Python 2.7-only).

Release-As: 2.0.0b1

Closes #777.
  • Loading branch information
tseaver committed Aug 3, 2021
1 parent 73cbc1f commit 560cf1e
Show file tree
Hide file tree
Showing 70 changed files with 350 additions and 714 deletions.
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

0 comments on commit 560cf1e

Please sign in to comment.