fix!: drop support for Python 2.7 / 3.5 (#212)
Drop 'six' module
Drop 'u"' prefixes for text
Remove other Python 2.7 workarounds
Drop use of 'pytz'
Dxpand range to allow 'google-auth' 2.x versions
Remove 'general_helpers.wraps': except for a backward-compatibility
import, 'functools.wraps' does everything wee need on Python >= 3.6.
Remove 'packaging' dependency
Release-As: 2.0.0b1
Closes #74.
Closes #215.
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index c7860ad..358404f 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -1,4 +1,3 @@
-.. Generated by synthtool. DO NOT EDIT!
############
Contributing
############
@@ -22,7 +21,7 @@
documentation.
- The feature must work fully on the following CPython versions:
- 2.7, 3.6, 3.7, 3.8 and 3.9 on both UNIX and Windows.
+ 3.6, 3.7, 3.8 and 3.9 on both UNIX and Windows.
- The feature must not add unnecessary dependencies (where
"unnecessary" is of course subjective, but new dependencies should
@@ -77,8 +76,8 @@
.. note::
- The unit tests and system tests are described in the
- ``noxfile.py`` files in each directory.
+ The unit tests tests are described in the ``noxfile.py`` files
+ in each directory.
.. nox: https://pypi.org/project/nox/
@@ -133,29 +132,6 @@
"Function-Under-Test"), which is PEP8-incompliant, but more readable.
Some also use a local variable, ``MUT`` (short for "Module-Under-Test").
-********************
-Running System Tests
-********************
-
-- To run system tests, you can execute::
-
- # Run all system tests
- $ nox -s system
-
- # Run a single system test
- $ nox -s system-3.8 -- -k <name of test>
-
-
- .. note::
-
- System tests are only configured to run under Python 2.7 and 3.8.
- For expediency, we do not run them in older versions of Python 3.
-
- This alone will not run the tests. You'll need to change some local
- auth settings and change some configuration in your project to
- run all the tests.
-
-- System tests will be run against an actual project. You should use local credentials from gcloud when possible. See `Best practices for application authentication <https://cloud.google.com/docs/authentication/best-practices-applications#local_development_and_testing_with_the>`__. Some tests require a service account. For those tests see `Authenticating as a service account <https://cloud.google.com/docs/authentication/production>`__.
*************
Test Coverage
@@ -221,13 +197,11 @@
We support:
-- `Python 2.7`_
- `Python 3.6`_
- `Python 3.7`_
- `Python 3.8`_
- `Python 3.9`_
-.. _Python 2.7: https://docs.python.org/2.7/
.. _Python 3.6: https://docs.python.org/3.6/
.. _Python 3.7: https://docs.python.org/3.7/
.. _Python 3.8: https://docs.python.org/3.8/
@@ -239,7 +213,7 @@
.. _config: https://github.com/googleapis/python-api-core/blob/master/noxfile.py
-We also explicitly decided to support Python 3 beginning with version 2.7.
+We also explicitly decided to support Python 3 beginning with version 3.6.
Reasons for this include:
- Encouraging use of newest versions of Python 3
diff --git a/README.rst b/README.rst
index 244043e..d94f3e8 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,7 @@
Core Library for Google Client Libraries
========================================
-|pypi| |versions|
+|pypi| |versions|
This library is not meant to stand-alone. Instead it defines
common helpers used by all Google API clients. For more information, see the
@@ -16,8 +16,13 @@
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, Python == 3.5.
+
+The last version of this library compatible with Python 2.7 and 3.5 is
+`google-api_core==1.31.1`.
diff --git a/docs/auth.rst b/docs/auth.rst
index faf0228..a9b296d 100644
--- a/docs/auth.rst
+++ b/docs/auth.rst
@@ -103,25 +103,6 @@
.. _google-auth-guide: https://googleapis.dev/python/google-auth/latest/user-guide.html#service-account-private-key-files
-
-Google App Engine Standard First Generation Environment
--------------------------------------------------------
-
-These credentials are used only in the legacy Python 2.7
-`First Generation Standard Environment`_. All other App Engine
-runtimes use Compute Engine credentials.
-
-.. _First Generation Standard Environment: https://cloud.google.com/appengine/docs/standard/runtimes
-
-To create
-:class:`credentials <google.auth.app_engine.Credentials>`
-just for Google App Engine:
-
-.. code:: python
-
- from google.auth import app_engine
- credentials = app_engine.Credentials()
-
Google Compute Engine Environment
---------------------------------
diff --git a/google/api_core/bidi.py b/google/api_core/bidi.py
index be52d97..56a021a 100644
--- a/google/api_core/bidi.py
+++ b/google/api_core/bidi.py
@@ -17,11 +17,10 @@
import collections
import datetime
import logging
+import queue as queue_module
import threading
import time
-from six.moves import queue
-
from google.api_core import exceptions
_LOGGER = logging.getLogger(__name__)
@@ -71,7 +70,7 @@
CPU consumed by spinning is pretty minuscule.
Args:
- queue (queue.Queue): The request queue.
+ queue (queue_module.Queue): The request queue.
period (float): The number of seconds to wait for items from the queue
before checking if the RPC is cancelled. In practice, this
determines the maximum amount of time the request consumption
@@ -108,7 +107,7 @@
while True:
try:
item = self._queue.get(timeout=self._period)
- except queue.Empty:
+ except queue_module.Empty:
if not self._is_active():
_LOGGER.debug(
"Empty queue and inactive call, exiting request " "generator."
@@ -247,7 +246,7 @@
self._start_rpc = start_rpc
self._initial_request = initial_request
self._rpc_metadata = metadata
- self._request_queue = queue.Queue()
+ self._request_queue = queue_module.Queue()
self._request_generator = None
self._is_active = False
self._callbacks = []
@@ -645,6 +644,7 @@
# Keeping the lock throughout avoids that.
# In the future, we could use `Condition.wait_for` if we drop
# Python 2.7.
+ # See: https://github.com/googleapis/python-api-core/issues/211
with self._wake:
while self._paused:
_LOGGER.debug("paused, waiting for waking.")
diff --git a/google/api_core/client_info.py b/google/api_core/client_info.py
index adca5f3..d7f4367 100644
--- a/google/api_core/client_info.py
+++ b/google/api_core/client_info.py
@@ -42,7 +42,7 @@
Args:
python_version (str): The Python interpreter version, for example,
- ``'2.7.13'``.
+ ``'3.9.6'``.
grpc_version (Optional[str]): The gRPC library version.
api_core_version (str): The google-api-core library version.
gapic_version (Optional[str]): The sversion of gapic-generated client
diff --git a/google/api_core/client_options.py b/google/api_core/client_options.py
index 57000e9..be5523d 100644
--- a/google/api_core/client_options.py
+++ b/google/api_core/client_options.py
@@ -101,7 +101,7 @@
"""Construct a client options object from a mapping object.
Args:
- options (six.moves.collections_abc.Mapping): A mapping object with client options.
+ options (collections.abc.Mapping): A mapping object with client options.
See the docstring for ClientOptions for details on valid arguments.
"""
diff --git a/google/api_core/datetime_helpers.py b/google/api_core/datetime_helpers.py
index e52fb1d..78268ef 100644
--- a/google/api_core/datetime_helpers.py
+++ b/google/api_core/datetime_helpers.py
@@ -18,12 +18,10 @@
import datetime
import re
-import pytz
-
from google.protobuf import timestamp_pb2
-_UTC_EPOCH = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc)
+_UTC_EPOCH = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc)
_RFC3339_MICROS = "%Y-%m-%dT%H:%M:%S.%fZ"
_RFC3339_NO_FRACTION = "%Y-%m-%dT%H:%M:%S"
# datetime.strptime cannot handle nanosecond precision: parse w/ regex
@@ -83,9 +81,9 @@
int: Microseconds since the unix epoch.
"""
if not value.tzinfo:
- value = value.replace(tzinfo=pytz.utc)
+ value = value.replace(tzinfo=datetime.timezone.utc)
# Regardless of what timezone is on the value, convert it to UTC.
- value = value.astimezone(pytz.utc)
+ value = value.astimezone(datetime.timezone.utc)
# Convert the datetime to a microsecond timestamp.
return int(calendar.timegm(value.timetuple()) * 1e6) + value.microsecond
@@ -156,7 +154,7 @@
nanos = int(fraction) * (10 ** scale)
micros = nanos // 1000
- return bare_seconds.replace(microsecond=micros, tzinfo=pytz.utc)
+ return bare_seconds.replace(microsecond=micros, tzinfo=datetime.timezone.utc)
from_rfc3339_nanos = from_rfc3339 # from_rfc3339_nanos method was deprecated.
@@ -256,7 +254,7 @@
bare.minute,
bare.second,
nanosecond=nanos,
- tzinfo=pytz.UTC,
+ tzinfo=datetime.timezone.utc,
)
def timestamp_pb(self):
@@ -265,7 +263,11 @@
Returns:
(:class:`~google.protobuf.timestamp_pb2.Timestamp`): Timestamp message
"""
- inst = self if self.tzinfo is not None else self.replace(tzinfo=pytz.UTC)
+ inst = (
+ self
+ if self.tzinfo is not None
+ else self.replace(tzinfo=datetime.timezone.utc)
+ )
delta = inst - _UTC_EPOCH
seconds = int(delta.total_seconds())
nanos = self._nanosecond or self.microsecond * 1000
@@ -292,5 +294,5 @@
bare.minute,
bare.second,
nanosecond=stamp.nanos,
- tzinfo=pytz.UTC,
+ tzinfo=datetime.timezone.utc,
)
diff --git a/google/api_core/exceptions.py b/google/api_core/exceptions.py
index 412fc2e..13be917 100644
--- a/google/api_core/exceptions.py
+++ b/google/api_core/exceptions.py
@@ -21,8 +21,7 @@
from __future__ import absolute_import
from __future__ import unicode_literals
-import six
-from six.moves import http_client
+import http.client
try:
import grpc
@@ -56,7 +55,6 @@
pass
[email protected]_2_unicode_compatible
class RetryError(GoogleAPIError):
"""Raised when a function has exhausted all of its available retries.
@@ -92,9 +90,7 @@
return cls
[email protected]_2_unicode_compatible
[email protected]_metaclass(_GoogleAPICallErrorMeta)
-class GoogleAPICallError(GoogleAPIError):
+class GoogleAPICallError(GoogleAPIError, metaclass=_GoogleAPICallErrorMeta):
"""Base class for exceptions raised by calling API methods.
Args:
@@ -153,25 +149,25 @@
class MovedPermanently(Redirection):
"""Exception mapping a ``301 Moved Permanently`` response."""
- code = http_client.MOVED_PERMANENTLY
+ code = http.client.MOVED_PERMANENTLY
class NotModified(Redirection):
"""Exception mapping a ``304 Not Modified`` response."""
- code = http_client.NOT_MODIFIED
+ code = http.client.NOT_MODIFIED
class TemporaryRedirect(Redirection):
"""Exception mapping a ``307 Temporary Redirect`` response."""
- code = http_client.TEMPORARY_REDIRECT
+ code = http.client.TEMPORARY_REDIRECT
class ResumeIncomplete(Redirection):
"""Exception mapping a ``308 Resume Incomplete`` response.
- .. note:: :attr:`http_client.PERMANENT_REDIRECT` is ``308``, but Google
+ .. note:: :attr:`http.client.PERMANENT_REDIRECT` is ``308``, but Google
APIs differ in their use of this status code.
"""
@@ -185,7 +181,7 @@
class BadRequest(ClientError):
"""Exception mapping a ``400 Bad Request`` response."""
- code = http_client.BAD_REQUEST
+ code = http.client.BAD_REQUEST
class InvalidArgument(BadRequest):
@@ -210,7 +206,7 @@
class Unauthorized(ClientError):
"""Exception mapping a ``401 Unauthorized`` response."""
- code = http_client.UNAUTHORIZED
+ code = http.client.UNAUTHORIZED
class Unauthenticated(Unauthorized):
@@ -222,7 +218,7 @@
class Forbidden(ClientError):
"""Exception mapping a ``403 Forbidden`` response."""
- code = http_client.FORBIDDEN
+ code = http.client.FORBIDDEN
class PermissionDenied(Forbidden):
@@ -235,20 +231,20 @@
"""Exception mapping a ``404 Not Found`` response or a
:attr:`grpc.StatusCode.NOT_FOUND` error."""
- code = http_client.NOT_FOUND
+ code = http.client.NOT_FOUND
grpc_status_code = grpc.StatusCode.NOT_FOUND if grpc is not None else None
class MethodNotAllowed(ClientError):
"""Exception mapping a ``405 Method Not Allowed`` response."""
- code = http_client.METHOD_NOT_ALLOWED
+ code = http.client.METHOD_NOT_ALLOWED
class Conflict(ClientError):
"""Exception mapping a ``409 Conflict`` response."""
- code = http_client.CONFLICT
+ code = http.client.CONFLICT
class AlreadyExists(Conflict):
@@ -266,26 +262,25 @@
class LengthRequired(ClientError):
"""Exception mapping a ``411 Length Required`` response."""
- code = http_client.LENGTH_REQUIRED
+ code = http.client.LENGTH_REQUIRED
class PreconditionFailed(ClientError):
"""Exception mapping a ``412 Precondition Failed`` response."""
- code = http_client.PRECONDITION_FAILED
+ code = http.client.PRECONDITION_FAILED
class RequestRangeNotSatisfiable(ClientError):
"""Exception mapping a ``416 Request Range Not Satisfiable`` response."""
- code = http_client.REQUESTED_RANGE_NOT_SATISFIABLE
+ code = http.client.REQUESTED_RANGE_NOT_SATISFIABLE
class TooManyRequests(ClientError):
"""Exception mapping a ``429 Too Many Requests`` response."""
- # http_client does not define a constant for this in Python 2.
- code = 429
+ code = http.client.TOO_MANY_REQUESTS
class ResourceExhausted(TooManyRequests):
@@ -312,7 +307,7 @@
"""Exception mapping a ``500 Internal Server Error`` response. or a
:attr:`grpc.StatusCode.INTERNAL` error."""
- code = http_client.INTERNAL_SERVER_ERROR
+ code = http.client.INTERNAL_SERVER_ERROR
grpc_status_code = grpc.StatusCode.INTERNAL if grpc is not None else None
@@ -332,28 +327,28 @@
"""Exception mapping a ``501 Not Implemented`` response or a
:attr:`grpc.StatusCode.UNIMPLEMENTED` error."""
- code = http_client.NOT_IMPLEMENTED
+ code = http.client.NOT_IMPLEMENTED
grpc_status_code = grpc.StatusCode.UNIMPLEMENTED if grpc is not None else None
class BadGateway(ServerError):
"""Exception mapping a ``502 Bad Gateway`` response."""
- code = http_client.BAD_GATEWAY
+ code = http.client.BAD_GATEWAY
class ServiceUnavailable(ServerError):
"""Exception mapping a ``503 Service Unavailable`` response or a
:attr:`grpc.StatusCode.UNAVAILABLE` error."""
- code = http_client.SERVICE_UNAVAILABLE
+ code = http.client.SERVICE_UNAVAILABLE
grpc_status_code = grpc.StatusCode.UNAVAILABLE if grpc is not None else None
class GatewayTimeout(ServerError):
"""Exception mapping a ``504 Gateway Timeout`` response."""
- code = http_client.GATEWAY_TIMEOUT
+ code = http.client.GATEWAY_TIMEOUT
class DeadlineExceeded(GatewayTimeout):
diff --git a/google/api_core/future/base.py b/google/api_core/future/base.py
index e7888ca..f300586 100644
--- a/google/api_core/future/base.py
+++ b/google/api_core/future/base.py
@@ -16,11 +16,8 @@
import abc
-import six
-
[email protected]_metaclass(abc.ABCMeta)
-class Future(object):
+class Future(object, metaclass=abc.ABCMeta):
# pylint: disable=missing-docstring
# We inherit the interfaces here from concurrent.futures.
diff --git a/google/api_core/gapic_v1/__init__.py b/google/api_core/gapic_v1/__init__.py
index 6632047..e5b7ad3 100644
--- a/google/api_core/gapic_v1/__init__.py
+++ b/google/api_core/gapic_v1/__init__.py
@@ -12,18 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import sys
-
from google.api_core.gapic_v1 import client_info
from google.api_core.gapic_v1 import config
+from google.api_core.gapic_v1 import config_async
from google.api_core.gapic_v1 import method
+from google.api_core.gapic_v1 import method_async
from google.api_core.gapic_v1 import routing_header
-__all__ = ["client_info", "config", "method", "routing_header"]
-
-if sys.version_info >= (3, 6):
- from google.api_core.gapic_v1 import config_async # noqa: F401
- from google.api_core.gapic_v1 import method_async # noqa: F401
-
- __all__.append("config_async")
- __all__.append("method_async")
+__all__ = [
+ "client_info",
+ "config",
+ "config_async",
+ "method",
+ "method_async",
+ "routing_header",
+]
diff --git a/google/api_core/gapic_v1/client_info.py b/google/api_core/gapic_v1/client_info.py
index bdc2ce4..fab0f54 100644
--- a/google/api_core/gapic_v1/client_info.py
+++ b/google/api_core/gapic_v1/client_info.py
@@ -33,7 +33,7 @@
Args:
python_version (str): The Python interpreter version, for example,
- ``'2.7.13'``.
+ ``'3.9.6'``.
grpc_version (Optional[str]): The gRPC library version.
api_core_version (str): The google-api-core library version.
gapic_version (Optional[str]): The sversion of gapic-generated client
diff --git a/google/api_core/gapic_v1/config.py b/google/api_core/gapic_v1/config.py
index 29e8645..9c72287 100644
--- a/google/api_core/gapic_v1/config.py
+++ b/google/api_core/gapic_v1/config.py
@@ -21,7 +21,6 @@
import collections
import grpc
-import six
from google.api_core import exceptions
from google.api_core import retry
@@ -130,24 +129,20 @@
# Grab all the retry codes
retry_codes_map = {
name: retry_codes
- for name, retry_codes in six.iteritems(interface_config.get("retry_codes", {}))
+ for name, retry_codes in interface_config.get("retry_codes", {}).items()
}
# Grab all of the retry params
retry_params_map = {
name: retry_params
- for name, retry_params in six.iteritems(
- interface_config.get("retry_params", {})
- )
+ for name, retry_params in interface_config.get("retry_params", {}).items()
}
# Iterate through all the API methods and create a flat MethodConfig
# instance for each one.
method_configs = {}
- for method_name, method_params in six.iteritems(
- interface_config.get("methods", {})
- ):
+ for method_name, method_params in interface_config.get("methods", {}).items():
retry_params_name = method_params.get("retry_params_name")
if retry_params_name is not None:
diff --git a/google/api_core/gapic_v1/method.py b/google/api_core/gapic_v1/method.py
index 8bf8256..79722c0 100644
--- a/google/api_core/gapic_v1/method.py
+++ b/google/api_core/gapic_v1/method.py
@@ -18,7 +18,8 @@
pagination, and long-running operations to gRPC methods.
"""
-from google.api_core import general_helpers
+import functools
+
from google.api_core import grpc_helpers
from google.api_core import timeout
from google.api_core.gapic_v1 import client_info
@@ -110,26 +111,22 @@
self._timeout = timeout
self._metadata = metadata
- def __call__(self, *args, **kwargs):
+ def __call__(self, *args, timeout=DEFAULT, retry=DEFAULT, **kwargs):
"""Invoke the low-level RPC with retry, timeout, and metadata."""
- # Note: Due to Python 2 lacking keyword-only arguments we use kwargs to
- # extract the retry and timeout params.
- timeout_ = _determine_timeout(
+ timeout = _determine_timeout(
self._timeout,
- kwargs.pop("timeout", self._timeout),
+ timeout,
# Use only the invocation-specified retry only for this, as we only
# want to adjust the timeout deadline if the *user* specified
# a different retry.
- kwargs.get("retry", None),
+ retry,
)
- retry = kwargs.pop("retry", self._retry)
-
if retry is DEFAULT:
retry = self._retry
# Apply all applicable decorators.
- wrapped_func = _apply_decorators(self._target, [retry, timeout_])
+ wrapped_func = _apply_decorators(self._target, [retry, timeout])
# Add the user agent metadata to the call.
if self._metadata is not None:
@@ -237,7 +234,7 @@
else:
user_agent_metadata = None
- return general_helpers.wraps(func)(
+ return functools.wraps(func)(
_GapicCallable(
func, default_retry, default_timeout, metadata=user_agent_metadata
)
diff --git a/google/api_core/gapic_v1/method_async.py b/google/api_core/gapic_v1/method_async.py
index 76e5757..84c99aa 100644
--- a/google/api_core/gapic_v1/method_async.py
+++ b/google/api_core/gapic_v1/method_async.py
@@ -17,7 +17,9 @@
pagination, and long-running operations to gRPC methods.
"""
-from google.api_core import general_helpers, grpc_helpers_async
+import functools
+
+from google.api_core import grpc_helpers_async
from google.api_core.gapic_v1 import client_info
from google.api_core.gapic_v1.method import _GapicCallable
from google.api_core.gapic_v1.method import DEFAULT # noqa: F401
@@ -41,6 +43,6 @@
metadata = [client_info.to_grpc_metadata()] if client_info is not None else None
- return general_helpers.wraps(func)(
+ return functools.wraps(func)(
_GapicCallable(func, default_retry, default_timeout, metadata=metadata)
)
diff --git a/google/api_core/gapic_v1/routing_header.py b/google/api_core/gapic_v1/routing_header.py
index 3fb12a6..a7bcb5a 100644
--- a/google/api_core/gapic_v1/routing_header.py
+++ b/google/api_core/gapic_v1/routing_header.py
@@ -20,9 +20,7 @@
Generally, these headers are specified as gRPC metadata.
"""
-import sys
-
-from six.moves.urllib.parse import urlencode
+from urllib.parse import urlencode
ROUTING_METADATA_KEY = "x-goog-request-params"
@@ -37,9 +35,6 @@
Returns:
str: The routing header string.
"""
- if sys.version_info[0] < 3:
- # Python 2 does not have the "safe" parameter for urlencode.
- return urlencode(params).replace("%2F", "/")
return urlencode(
params,
# Per Google API policy (go/api-url-encoding), / is not encoded.
diff --git a/google/api_core/general_helpers.py b/google/api_core/general_helpers.py
index d2d0c44..fba7802 100644
--- a/google/api_core/general_helpers.py
+++ b/google/api_core/general_helpers.py
@@ -12,22 +12,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-"""Helpers for general Python functionality."""
-
-import functools
-
-import six
-
-
-# functools.partial objects lack several attributes present on real function
-# objects. In Python 2 wraps fails on this so use a restricted set instead.
-_PARTIAL_VALID_ASSIGNMENTS = ("__doc__",)
-
-
-def wraps(wrapped):
- """A functools.wraps helper that handles partial objects on Python 2."""
- # https://github.com/google/pytype/issues/322
- if isinstance(wrapped, functools.partial): # pytype: disable=wrong-arg-types
- return six.wraps(wrapped, assigned=_PARTIAL_VALID_ASSIGNMENTS)
- else:
- return six.wraps(wrapped)
+# This import for backward compatibiltiy only.
+from functools import wraps # noqa: F401 pragma: NO COVER
diff --git a/google/api_core/grpc_helpers.py b/google/api_core/grpc_helpers.py
index 62d9e53..594df98 100644
--- a/google/api_core/grpc_helpers.py
+++ b/google/api_core/grpc_helpers.py
@@ -15,13 +15,12 @@
"""Helpers for :mod:`grpc`."""
import collections
+import functools
import grpc
import pkg_resources
-import six
from google.api_core import exceptions
-from google.api_core import general_helpers
import google.auth
import google.auth.credentials
import google.auth.transport.grpc
@@ -61,12 +60,12 @@
"""Map errors for Unary-Unary and Stream-Unary gRPC callables."""
_patch_callable_name(callable_)
- @six.wraps(callable_)
+ @functools.wraps(callable_)
def error_remapped_callable(*args, **kwargs):
try:
return callable_(*args, **kwargs)
except grpc.RpcError as exc:
- six.raise_from(exceptions.from_grpc_error(exc), exc)
+ raise exceptions.from_grpc_error(exc) from exc
return error_remapped_callable
@@ -80,7 +79,7 @@
# to retrieve the first result, in order to fail, in order to trigger a retry.
try:
if prefetch_first_result:
- self._stored_first_result = six.next(self._wrapped)
+ self._stored_first_result = next(self._wrapped)
except TypeError:
# It is possible the wrapped method isn't an iterable (a grpc.Call
# for instance). If this happens don't store the first result.
@@ -93,7 +92,7 @@
"""This iterator is also an iterable that returns itself."""
return self
- def next(self):
+ def __next__(self):
"""Get the next response from the stream.
Returns:
@@ -104,13 +103,10 @@
result = self._stored_first_result
del self._stored_first_result
return result
- return six.next(self._wrapped)
+ return next(self._wrapped)
except grpc.RpcError as exc:
# If the stream has already returned data, we cannot recover here.
- six.raise_from(exceptions.from_grpc_error(exc), exc)
-
- # Alias needed for Python 2/3 support.
- __next__ = next
+ raise exceptions.from_grpc_error(exc) from exc
# grpc.Call & grpc.RpcContext interface
@@ -148,7 +144,7 @@
"""
_patch_callable_name(callable_)
- @general_helpers.wraps(callable_)
+ @functools.wraps(callable_)
def error_remapped_callable(*args, **kwargs):
try:
result = callable_(*args, **kwargs)
@@ -161,7 +157,7 @@
result, prefetch_first_result=prefetch_first
)
except grpc.RpcError as exc:
- six.raise_from(exceptions.from_grpc_error(exc), exc)
+ raise exceptions.from_grpc_error(exc) from exc
return error_remapped_callable
diff --git a/google/api_core/iam.py b/google/api_core/iam.py
index 59e5387..4437c70 100644
--- a/google/api_core/iam.py
+++ b/google/api_core/iam.py
@@ -52,14 +52,10 @@
"""
import collections
+import collections.abc
import operator
import warnings
-try:
- from collections import abc as collections_abc
-except ImportError: # Python 2.7
- import collections as collections_abc
-
# Generic IAM roles
OWNER_ROLE = "roles/owner"
@@ -84,7 +80,7 @@
pass
-class Policy(collections_abc.MutableMapping):
+class Policy(collections.abc.MutableMapping):
"""IAM Policy
Args:
diff --git a/google/api_core/operations_v1/__init__.py b/google/api_core/operations_v1/__init__.py
index bc9befc..d7f963e 100644
--- a/google/api_core/operations_v1/__init__.py
+++ b/google/api_core/operations_v1/__init__.py
@@ -14,11 +14,7 @@
"""Package for interacting with the google.longrunning.operations meta-API."""
-import sys
-
+from google.api_core.operations_v1.operations_async_client import OperationsAsyncClient
from google.api_core.operations_v1.operations_client import OperationsClient
-__all__ = ["OperationsClient"]
-if sys.version_info >= (3, 6, 0):
- from google.api_core.operations_v1.operations_async_client import OperationsAsyncClient # noqa: F401
- __all__.append("OperationsAsyncClient")
+__all__ = ["OperationsAsyncClient", "OperationsClient"]
diff --git a/google/api_core/page_iterator.py b/google/api_core/page_iterator.py
index 49879bc..7ddc5cb 100644
--- a/google/api_core/page_iterator.py
+++ b/google/api_core/page_iterator.py
@@ -81,8 +81,6 @@
import abc
-import six
-
class Page(object):
"""Single page of results in an iterator.
@@ -127,18 +125,15 @@
"""The :class:`Page` is an iterator of items."""
return self
- def next(self):
+ def __next__(self):
"""Get the next value in the page."""
- item = six.next(self._item_iter)
+ item = next(self._item_iter)
result = self._item_to_value(self._parent, item)
# Since we've successfully got the next value from the
# iterator, we update the number of remaining.
self._remaining -= 1
return result
- # Alias needed for Python 2/3 support.
- __next__ = next
-
def _item_to_value_identity(iterator, item):
"""An item to value transformer that returns the item un-changed."""
@@ -147,8 +142,7 @@
return item
[email protected]_metaclass(abc.ABCMeta)
-class Iterator(object):
+class Iterator(object, metaclass=abc.ABCMeta):
"""A generic class for iterating through API list responses.
Args:
@@ -235,9 +229,6 @@
self.__active_iterator = iter(self)
return next(self.__active_iterator)
- # Preserve Python 2 compatibility.
- next = __next__
-
def _page_iter(self, increment):
"""Generator of pages of API responses.
@@ -484,7 +475,7 @@
there are no pages left.
"""
try:
- items = six.next(self._gax_page_iter)
+ items = next(self._gax_page_iter)
page = Page(self, items, self.item_to_value)
self.next_page_token = self._gax_page_iter.page_token or None
return page
diff --git a/google/api_core/path_template.py b/google/api_core/path_template.py
index f202d40..c5969c1 100644
--- a/google/api_core/path_template.py
+++ b/google/api_core/path_template.py
@@ -28,8 +28,6 @@
import functools
import re
-import six
-
# Regular expression for extracting variable parts from a path template.
# The variables can be expressed as:
#
@@ -83,7 +81,7 @@
name = match.group("name")
if name is not None:
try:
- return six.text_type(named_vars[name])
+ return str(named_vars[name])
except KeyError:
raise ValueError(
"Named variable '{}' not specified and needed by template "
@@ -91,7 +89,7 @@
)
elif positional is not None:
try:
- return six.text_type(positional_vars.pop(0))
+ return str(positional_vars.pop(0))
except IndexError:
raise ValueError(
"Positional variable not specified and needed by template "
diff --git a/google/api_core/protobuf_helpers.py b/google/api_core/protobuf_helpers.py
index 8aff79a..896e89c 100644
--- a/google/api_core/protobuf_helpers.py
+++ b/google/api_core/protobuf_helpers.py
@@ -15,6 +15,7 @@
"""Helpers for :mod:`protobuf`."""
import collections
+import collections.abc
import copy
import inspect
@@ -22,11 +23,6 @@
from google.protobuf import message
from google.protobuf import wrappers_pb2
-try:
- from collections import abc as collections_abc
-except ImportError: # Python 2.7
- import collections as collections_abc
-
_SENTINEL = object()
_WRAPPER_TYPES = (
@@ -179,7 +175,7 @@
# If we get something else, complain.
if isinstance(msg_or_dict, message.Message):
answer = getattr(msg_or_dict, key, default)
- elif isinstance(msg_or_dict, collections_abc.Mapping):
+ elif isinstance(msg_or_dict, collections.abc.Mapping):
answer = msg_or_dict.get(key, default)
else:
raise TypeError(
@@ -204,7 +200,7 @@
"""Set helper for protobuf Messages."""
# Attempt to set the value on the types of objects we know how to deal
# with.
- if isinstance(value, (collections_abc.MutableSequence, tuple)):
+ if isinstance(value, (collections.abc.MutableSequence, tuple)):
# Clear the existing repeated protobuf message of any elements
# currently inside it.
while getattr(msg, key):
@@ -212,13 +208,13 @@
# Write our new elements to the repeated field.
for item in value:
- if isinstance(item, collections_abc.Mapping):
+ if isinstance(item, collections.abc.Mapping):
getattr(msg, key).add(**item)
else:
# protobuf's RepeatedCompositeContainer doesn't support
# append.
getattr(msg, key).extend([item])
- elif isinstance(value, collections_abc.Mapping):
+ elif isinstance(value, collections.abc.Mapping):
# Assign the dictionary values to the protobuf message.
for item_key, item_value in value.items():
set(getattr(msg, key), item_key, item_value)
@@ -241,7 +237,7 @@
TypeError: If ``msg_or_dict`` is not a Message or dictionary.
"""
# Sanity check: Is our target object valid?
- if not isinstance(msg_or_dict, (collections_abc.MutableMapping, message.Message)):
+ if not isinstance(msg_or_dict, (collections.abc.MutableMapping, message.Message)):
raise TypeError(
"set() expected a dict or protobuf message, got {!r}.".format(
type(msg_or_dict)
@@ -254,12 +250,12 @@
# If a subkey exists, then get that object and call this method
# recursively against it using the subkey.
if subkey is not None:
- if isinstance(msg_or_dict, collections_abc.MutableMapping):
+ if isinstance(msg_or_dict, collections.abc.MutableMapping):
msg_or_dict.setdefault(basekey, {})
set(get(msg_or_dict, basekey), subkey, value)
return
- if isinstance(msg_or_dict, collections_abc.MutableMapping):
+ if isinstance(msg_or_dict, collections.abc.MutableMapping):
msg_or_dict[key] = value
else:
_set_field_on_message(msg_or_dict, key, value)
diff --git a/google/api_core/retry.py b/google/api_core/retry.py
index 8496793..d39f97c 100644
--- a/google/api_core/retry.py
+++ b/google/api_core/retry.py
@@ -63,11 +63,9 @@
import time
import requests.exceptions
-import six
from google.api_core import datetime_helpers
from google.api_core import exceptions
-from google.api_core import general_helpers
from google.auth import exceptions as auth_exceptions
_LOGGER = logging.getLogger(__name__)
@@ -201,15 +199,12 @@
if deadline_datetime is not None:
if deadline_datetime <= now:
- six.raise_from(
- exceptions.RetryError(
- "Deadline of {:.1f}s exceeded while calling {}".format(
- deadline, target
- ),
- last_exc,
+ raise exceptions.RetryError(
+ "Deadline of {:.1f}s exceeded while calling {}".format(
+ deadline, target
),
last_exc,
- )
+ ) from last_exc
else:
time_to_deadline = (deadline_datetime - now).total_seconds()
sleep = min(time_to_deadline, sleep)
@@ -222,7 +217,6 @@
raise ValueError("Sleep generator stopped yielding sleep values.")
[email protected]_2_unicode_compatible
class Retry(object):
"""Exponential retry decorator.
@@ -276,7 +270,7 @@
if self._on_error is not None:
on_error = self._on_error
- @general_helpers.wraps(func)
+ @functools.wraps(func)
def retry_wrapped_func(*args, **kwargs):
"""A wrapper that calls target function with retry."""
target = functools.partial(func, *args, **kwargs)
diff --git a/google/api_core/timeout.py b/google/api_core/timeout.py
index 17c1bea..7323218 100644
--- a/google/api_core/timeout.py
+++ b/google/api_core/timeout.py
@@ -54,11 +54,9 @@
from __future__ import unicode_literals
import datetime
-
-import six
+import functools
from google.api_core import datetime_helpers
-from google.api_core import general_helpers
_DEFAULT_INITIAL_TIMEOUT = 5.0 # seconds
_DEFAULT_MAXIMUM_TIMEOUT = 30.0 # seconds
@@ -68,7 +66,6 @@
_DEFAULT_DEADLINE = None
[email protected]_2_unicode_compatible
class ConstantTimeout(object):
"""A decorator that adds a constant timeout argument.
@@ -95,7 +92,7 @@
Callable: The wrapped function.
"""
- @general_helpers.wraps(func)
+ @functools.wraps(func)
def func_with_timeout(*args, **kwargs):
"""Wrapped function that adds timeout."""
kwargs["timeout"] = self._timeout
@@ -140,7 +137,6 @@
timeout = timeout * multiplier
[email protected]_2_unicode_compatible
class ExponentialTimeout(object):
"""A decorator that adds an exponentially increasing timeout argument.
@@ -207,7 +203,7 @@
self._initial, self._maximum, self._multiplier, self._deadline
)
- @general_helpers.wraps(func)
+ @functools.wraps(func)
def func_with_timeout(*args, **kwargs):
"""Wrapped function that adds timeout."""
kwargs["timeout"] = next(timeouts)
diff --git a/google/api_core/version.py b/google/api_core/version.py
index 5fd0b13..c9cdad6 100644
--- a/google/api_core/version.py
+++ b/google/api_core/version.py
@@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-__version__ = "1.31.1"
+__version__ = "2.0.0-b1"
diff --git a/noxfile.py b/noxfile.py
index 2f11137..a8f464e 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -111,13 +111,13 @@
session.run(*pytest_args)
[email protected](python=["2.7", "3.6", "3.7", "3.8", "3.9"])
[email protected](python=["3.6", "3.7", "3.8", "3.9"])
def unit(session):
"""Run the unit test suite."""
default(session)
[email protected](python=["2.7", "3.6", "3.7", "3.8", "3.9"])
[email protected](python=["3.6", "3.7", "3.8", "3.9"])
def unit_grpc_gcp(session):
"""Run the unit test suite with grpcio-gcp installed."""
constraints_path = str(
@@ -137,7 +137,6 @@
session.run("python", "setup.py", "check", "--restructuredtext", "--strict")
-# No 2.7 due to https://github.com/google/importlab/issues/26.
# No 3.7 because pytype supports up to 3.6 only.
@nox.session(python="3.6")
def pytype(session):
diff --git a/owlbot.py b/owlbot.py
index 5c590e4..451f7c4 100644
--- a/owlbot.py
+++ b/owlbot.py
@@ -22,8 +22,15 @@
# ----------------------------------------------------------------------------
# Add templated files
# ----------------------------------------------------------------------------
-templated_files = common.py_library(cov_level=100)
-s.move(templated_files, excludes=["noxfile.py", ".flake8", ".coveragerc", "setup.cfg"])
+excludes = [
+ "noxfile.py", # pytype
+ "setup.cfg", # pytype
+ ".flake8", # flake8-import-order, layout
+ ".coveragerc", # layout
+ "CONTRIBUTING.rst", # no systests
+]
+templated_files = common.py_library(microgenerator=True, cov_level=100)
+s.move(templated_files, excludes=excludes)
# Add pytype support
s.replace(
diff --git a/setup.py b/setup.py
index 26fab7e..d98c69c 100644
--- a/setup.py
+++ b/setup.py
@@ -31,12 +31,9 @@
dependencies = [
"googleapis-common-protos >= 1.6.0, < 2.0dev",
"protobuf >= 3.12.0",
- "google-auth >= 1.25.0, < 2.0dev",
+ "google-auth >= 1.25.0, < 3.0dev",
"requests >= 2.18.0, < 3.0.0dev",
"setuptools >= 40.3.0",
- "packaging >= 14.3",
- "six >= 1.13.0",
- "pytz",
'futures >= 3.2.0; python_version < "3.2"',
]
extras = {
@@ -86,10 +83,7 @@
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python",
- "Programming Language :: Python :: 2",
- "Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
@@ -102,7 +96,7 @@
namespace_packages=namespaces,
install_requires=dependencies,
extras_require=extras,
- python_requires=">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*",
+ python_requires=">=3.6",
include_package_data=True,
zip_safe=False,
)
diff --git a/testing/constraints-2.7.txt b/testing/constraints-2.7.txt
deleted file mode 100644
index 246c89d..0000000
--- a/testing/constraints-2.7.txt
+++ /dev/null
@@ -1 +0,0 @@
-googleapis-common-protos >= 1.6.0, < 1.53dev
diff --git a/testing/constraints-3.6.txt b/testing/constraints-3.6.txt
index ff5b4a7..2544e8e 100644
--- a/testing/constraints-3.6.txt
+++ b/testing/constraints-3.6.txt
@@ -11,7 +11,6 @@
requests==2.18.0
setuptools==40.3.0
packaging==14.3
-six==1.13.0
grpcio==1.29.0
grpcio-gcp==0.2.2
grpcio-gcp==0.2.2
diff --git a/tests/unit/test_bidi.py b/tests/unit/test_bidi.py
index 602d640..b876d9a 100644
--- a/tests/unit/test_bidi.py
+++ b/tests/unit/test_bidi.py
@@ -14,12 +14,12 @@
import datetime
import logging
+import queue
import threading
import grpc
import mock
import pytest
-from six.moves import queue
from google.api_core import bidi
from google.api_core import exceptions
@@ -221,18 +221,12 @@
class ClosedCall(object):
- # NOTE: This is needed because defining `.next` on an **instance**
- # rather than the **class** will not be iterable in Python 2.
- # This is problematic since a `Mock` just sets members.
-
def __init__(self, exception):
self.exception = exception
def __next__(self):
raise self.exception
- next = __next__ # Python 2
-
def is_active(self):
return False
@@ -354,8 +348,6 @@
raise item
return item
- next = __next__ # Python 2
-
def is_active(self):
return self._is_active
diff --git a/tests/unit/test_datetime_helpers.py b/tests/unit/test_datetime_helpers.py
index 4ddcf36..5f5470a 100644
--- a/tests/unit/test_datetime_helpers.py
+++ b/tests/unit/test_datetime_helpers.py
@@ -16,7 +16,6 @@
import datetime
import pytest
-import pytz
from google.api_core import datetime_helpers
from google.protobuf import timestamp_pb2
@@ -31,7 +30,7 @@
def test_to_milliseconds():
- dt = datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=pytz.utc)
+ dt = datetime.datetime(1970, 1, 1, 0, 0, 1, tzinfo=datetime.timezone.utc)
assert datetime_helpers.to_milliseconds(dt) == 1000
@@ -42,7 +41,7 @@
def test_to_microseconds_non_utc():
- zone = pytz.FixedOffset(-1)
+ zone = datetime.timezone(datetime.timedelta(minutes=-1))
dt = datetime.datetime(1970, 1, 1, 0, 0, 0, tzinfo=zone)
assert datetime_helpers.to_microseconds(dt) == ONE_MINUTE_IN_MICROSECONDS
@@ -56,7 +55,7 @@
def test_from_microseconds():
five_mins_from_epoch_in_microseconds = 5 * ONE_MINUTE_IN_MICROSECONDS
five_mins_from_epoch_datetime = datetime.datetime(
- 1970, 1, 1, 0, 5, 0, tzinfo=pytz.utc
+ 1970, 1, 1, 0, 5, 0, tzinfo=datetime.timezone.utc
)
result = datetime_helpers.from_microseconds(five_mins_from_epoch_in_microseconds)
@@ -78,28 +77,28 @@
def test_from_rfc3339():
value = "2009-12-17T12:44:32.123456Z"
assert datetime_helpers.from_rfc3339(value) == datetime.datetime(
- 2009, 12, 17, 12, 44, 32, 123456, pytz.utc
+ 2009, 12, 17, 12, 44, 32, 123456, datetime.timezone.utc
)
def test_from_rfc3339_nanos():
value = "2009-12-17T12:44:32.123456Z"
assert datetime_helpers.from_rfc3339_nanos(value) == datetime.datetime(
- 2009, 12, 17, 12, 44, 32, 123456, pytz.utc
+ 2009, 12, 17, 12, 44, 32, 123456, datetime.timezone.utc
)
def test_from_rfc3339_without_nanos():
value = "2009-12-17T12:44:32Z"
assert datetime_helpers.from_rfc3339(value) == datetime.datetime(
- 2009, 12, 17, 12, 44, 32, 0, pytz.utc
+ 2009, 12, 17, 12, 44, 32, 0, datetime.timezone.utc
)
def test_from_rfc3339_nanos_without_nanos():
value = "2009-12-17T12:44:32Z"
assert datetime_helpers.from_rfc3339_nanos(value) == datetime.datetime(
- 2009, 12, 17, 12, 44, 32, 0, pytz.utc
+ 2009, 12, 17, 12, 44, 32, 0, datetime.timezone.utc
)
@@ -119,7 +118,7 @@
def test_from_rfc3339_with_truncated_nanos(truncated, micros):
value = "2009-12-17T12:44:32.{}Z".format(truncated)
assert datetime_helpers.from_rfc3339(value) == datetime.datetime(
- 2009, 12, 17, 12, 44, 32, micros, pytz.utc
+ 2009, 12, 17, 12, 44, 32, micros, datetime.timezone.utc
)
@@ -148,7 +147,7 @@
def test_from_rfc3339_nanos_with_truncated_nanos(truncated, micros):
value = "2009-12-17T12:44:32.{}Z".format(truncated)
assert datetime_helpers.from_rfc3339_nanos(value) == datetime.datetime(
- 2009, 12, 17, 12, 44, 32, micros, pytz.utc
+ 2009, 12, 17, 12, 44, 32, micros, datetime.timezone.utc
)
@@ -171,20 +170,20 @@
def test_to_rfc3339_with_utc():
- value = datetime.datetime(2016, 4, 5, 13, 30, 0, tzinfo=pytz.utc)
+ value = datetime.datetime(2016, 4, 5, 13, 30, 0, tzinfo=datetime.timezone.utc)
expected = "2016-04-05T13:30:00.000000Z"
assert datetime_helpers.to_rfc3339(value, ignore_zone=False) == expected
def test_to_rfc3339_with_non_utc():
- zone = pytz.FixedOffset(-60)
+ zone = datetime.timezone(datetime.timedelta(minutes=-60))
value = datetime.datetime(2016, 4, 5, 13, 30, 0, tzinfo=zone)
expected = "2016-04-05T14:30:00.000000Z"
assert datetime_helpers.to_rfc3339(value, ignore_zone=False) == expected
def test_to_rfc3339_with_non_utc_ignore_zone():
- zone = pytz.FixedOffset(-60)
+ zone = datetime.timezone(datetime.timedelta(minutes=-60))
value = datetime.datetime(2016, 4, 5, 13, 30, 0, tzinfo=zone)
expected = "2016-04-05T13:30:00.000000Z"
assert datetime_helpers.to_rfc3339(value, ignore_zone=True) == expected
@@ -283,7 +282,7 @@
def test_from_rfc3339_wo_fraction():
timestamp = "2016-12-20T21:13:47Z"
expected = datetime_helpers.DatetimeWithNanoseconds(
- 2016, 12, 20, 21, 13, 47, tzinfo=pytz.UTC
+ 2016, 12, 20, 21, 13, 47, tzinfo=datetime.timezone.utc
)
stamp = datetime_helpers.DatetimeWithNanoseconds.from_rfc3339(timestamp)
assert stamp == expected
@@ -292,7 +291,7 @@
def test_from_rfc3339_w_partial_precision():
timestamp = "2016-12-20T21:13:47.1Z"
expected = datetime_helpers.DatetimeWithNanoseconds(
- 2016, 12, 20, 21, 13, 47, microsecond=100000, tzinfo=pytz.UTC
+ 2016, 12, 20, 21, 13, 47, microsecond=100000, tzinfo=datetime.timezone.utc
)
stamp = datetime_helpers.DatetimeWithNanoseconds.from_rfc3339(timestamp)
assert stamp == expected
@@ -301,7 +300,7 @@
def test_from_rfc3339_w_full_precision():
timestamp = "2016-12-20T21:13:47.123456789Z"
expected = datetime_helpers.DatetimeWithNanoseconds(
- 2016, 12, 20, 21, 13, 47, nanosecond=123456789, tzinfo=pytz.UTC
+ 2016, 12, 20, 21, 13, 47, nanosecond=123456789, tzinfo=datetime.timezone.utc
)
stamp = datetime_helpers.DatetimeWithNanoseconds.from_rfc3339(timestamp)
assert stamp == expected
@@ -332,7 +331,9 @@
stamp = datetime_helpers.DatetimeWithNanoseconds(
2016, 12, 20, 21, 13, 47, 123456
)
- delta = stamp.replace(tzinfo=pytz.UTC) - datetime_helpers._UTC_EPOCH
+ delta = (
+ stamp.replace(tzinfo=datetime.timezone.utc) - datetime_helpers._UTC_EPOCH
+ )
seconds = int(delta.total_seconds())
nanos = 123456000
timestamp = timestamp_pb2.Timestamp(seconds=seconds, nanos=nanos)
@@ -341,7 +342,7 @@
@staticmethod
def test_timestamp_pb_w_nanos():
stamp = datetime_helpers.DatetimeWithNanoseconds(
- 2016, 12, 20, 21, 13, 47, nanosecond=123456789, tzinfo=pytz.UTC
+ 2016, 12, 20, 21, 13, 47, nanosecond=123456789, tzinfo=datetime.timezone.utc
)
delta = stamp - datetime_helpers._UTC_EPOCH
timestamp = timestamp_pb2.Timestamp(
@@ -351,7 +352,9 @@
@staticmethod
def test_from_timestamp_pb_wo_nanos():
- when = datetime.datetime(2016, 12, 20, 21, 13, 47, 123456, tzinfo=pytz.UTC)
+ when = datetime.datetime(
+ 2016, 12, 20, 21, 13, 47, 123456, tzinfo=datetime.timezone.utc
+ )
delta = when - datetime_helpers._UTC_EPOCH
seconds = int(delta.total_seconds())
timestamp = timestamp_pb2.Timestamp(seconds=seconds)
@@ -361,11 +364,13 @@
assert _to_seconds(when) == _to_seconds(stamp)
assert stamp.microsecond == 0
assert stamp.nanosecond == 0
- assert stamp.tzinfo == pytz.UTC
+ assert stamp.tzinfo == datetime.timezone.utc
@staticmethod
def test_from_timestamp_pb_w_nanos():
- when = datetime.datetime(2016, 12, 20, 21, 13, 47, 123456, tzinfo=pytz.UTC)
+ when = datetime.datetime(
+ 2016, 12, 20, 21, 13, 47, 123456, tzinfo=datetime.timezone.utc
+ )
delta = when - datetime_helpers._UTC_EPOCH
seconds = int(delta.total_seconds())
timestamp = timestamp_pb2.Timestamp(seconds=seconds, nanos=123456789)
@@ -375,7 +380,7 @@
assert _to_seconds(when) == _to_seconds(stamp)
assert stamp.microsecond == 123456
assert stamp.nanosecond == 123456789
- assert stamp.tzinfo == pytz.UTC
+ assert stamp.tzinfo == datetime.timezone.utc
def _to_seconds(value):
@@ -387,5 +392,5 @@
Returns:
int: Microseconds since the unix epoch.
"""
- assert value.tzinfo is pytz.UTC
+ assert value.tzinfo is datetime.timezone.utc
return calendar.timegm(value.timetuple())
diff --git a/tests/unit/test_exceptions.py b/tests/unit/test_exceptions.py
index fb29015..1059945 100644
--- a/tests/unit/test_exceptions.py
+++ b/tests/unit/test_exceptions.py
@@ -12,12 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import http.client
import json
import grpc
import mock
import requests
-from six.moves import http_client
from google.api_core import exceptions
@@ -50,8 +50,8 @@
def test_from_http_status():
message = "message"
- exception = exceptions.from_http_status(http_client.NOT_FOUND, message)
- assert exception.code == http_client.NOT_FOUND
+ exception = exceptions.from_http_status(http.client.NOT_FOUND, message)
+ assert exception.code == http.client.NOT_FOUND
assert exception.message == message
assert exception.errors == []
@@ -61,11 +61,11 @@
errors = ["1", "2"]
response = mock.sentinel.response
exception = exceptions.from_http_status(
- http_client.NOT_FOUND, message, errors=errors, response=response
+ http.client.NOT_FOUND, message, errors=errors, response=response
)
assert isinstance(exception, exceptions.NotFound)
- assert exception.code == http_client.NOT_FOUND
+ assert exception.code == http.client.NOT_FOUND
assert exception.message == message
assert exception.errors == errors
assert exception.response == response
@@ -82,7 +82,7 @@
def make_response(content):
response = requests.Response()
response._content = content
- response.status_code = http_client.NOT_FOUND
+ response.status_code = http.client.NOT_FOUND
response.request = requests.Request(
method="POST", url="https://example.com"
).prepare()
@@ -95,7 +95,7 @@
exception = exceptions.from_http_response(response)
assert isinstance(exception, exceptions.NotFound)
- assert exception.code == http_client.NOT_FOUND
+ assert exception.code == http.client.NOT_FOUND
assert exception.message == "POST https://example.com/: unknown error"
assert exception.response == response
@@ -106,7 +106,7 @@
exception = exceptions.from_http_response(response)
assert isinstance(exception, exceptions.NotFound)
- assert exception.code == http_client.NOT_FOUND
+ assert exception.code == http.client.NOT_FOUND
assert exception.message == "POST https://example.com/: message"
@@ -120,7 +120,7 @@
exception = exceptions.from_http_response(response)
assert isinstance(exception, exceptions.NotFound)
- assert exception.code == http_client.NOT_FOUND
+ assert exception.code == http.client.NOT_FOUND
assert exception.message == "POST https://example.com/: json message"
assert exception.errors == ["1", "2"]
@@ -131,22 +131,22 @@
exception = exceptions.from_http_response(response)
assert isinstance(exception, exceptions.NotFound)
- assert exception.code == http_client.NOT_FOUND
+ assert exception.code == http.client.NOT_FOUND
assert exception.message == "POST https://example.com/: unknown error"
def test_from_http_response_json_unicode_content():
response = make_response(
json.dumps(
- {"error": {"message": u"\u2019 message", "errors": ["1", "2"]}}
+ {"error": {"message": "\u2019 message", "errors": ["1", "2"]}}
).encode("utf-8")
)
exception = exceptions.from_http_response(response)
assert isinstance(exception, exceptions.NotFound)
- assert exception.code == http_client.NOT_FOUND
- assert exception.message == u"POST https://example.com/: \u2019 message"
+ assert exception.code == http.client.NOT_FOUND
+ assert exception.message == "POST https://example.com/: \u2019 message"
assert exception.errors == ["1", "2"]
@@ -155,7 +155,7 @@
exception = exceptions.from_grpc_status(grpc.StatusCode.OUT_OF_RANGE, message)
assert isinstance(exception, exceptions.BadRequest)
assert isinstance(exception, exceptions.OutOfRange)
- assert exception.code == http_client.BAD_REQUEST
+ assert exception.code == http.client.BAD_REQUEST
assert exception.grpc_status_code == grpc.StatusCode.OUT_OF_RANGE
assert exception.message == message
assert exception.errors == []
@@ -166,7 +166,7 @@
exception = exceptions.from_grpc_status(11, message)
assert isinstance(exception, exceptions.BadRequest)
assert isinstance(exception, exceptions.OutOfRange)
- assert exception.code == http_client.BAD_REQUEST
+ assert exception.code == http.client.BAD_REQUEST
assert exception.grpc_status_code == grpc.StatusCode.OUT_OF_RANGE
assert exception.message == message
assert exception.errors == []
@@ -203,7 +203,7 @@
assert isinstance(exception, exceptions.BadRequest)
assert isinstance(exception, exceptions.InvalidArgument)
- assert exception.code == http_client.BAD_REQUEST
+ assert exception.code == http.client.BAD_REQUEST
assert exception.grpc_status_code == grpc.StatusCode.INVALID_ARGUMENT
assert exception.message == message
assert exception.errors == [error]
diff --git a/tests/unit/test_general_helpers.py b/tests/unit/test_general_helpers.py
deleted file mode 100644
index 027d489..0000000
--- a/tests/unit/test_general_helpers.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2017, Google LLC
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import functools
-
-from google.api_core import general_helpers
-
-
-def test_wraps_normal_func():
- def func():
- return 42
-
- @general_helpers.wraps(func)
- def replacement():
- return func()
-
- assert replacement() == 42
-
-
-def test_wraps_partial():
- def func():
- return 42
-
- partial = functools.partial(func)
-
- @general_helpers.wraps(partial)
- def replacement():
- return func()
-
- assert replacement() == 42
diff --git a/tests/unit/test_page_iterator.py b/tests/unit/test_page_iterator.py
index 668cf39..a44e998 100644
--- a/tests/unit/test_page_iterator.py
+++ b/tests/unit/test_page_iterator.py
@@ -17,7 +17,6 @@
import mock
import pytest
-import six
from google.api_core import page_iterator
@@ -56,17 +55,17 @@
assert item_to_value.call_count == 0
assert page.remaining == 100
- assert six.next(page) == 10
+ assert next(page) == 10
assert item_to_value.call_count == 1
item_to_value.assert_called_with(parent, 10)
assert page.remaining == 99
- assert six.next(page) == 11
+ assert next(page) == 11
assert item_to_value.call_count == 2
item_to_value.assert_called_with(parent, 11)
assert page.remaining == 98
- assert six.next(page) == 12
+ assert next(page) == 12
assert item_to_value.call_count == 3
item_to_value.assert_called_with(parent, 12)
assert page.remaining == 97
@@ -197,17 +196,17 @@
# Consume items and check the state of the iterator.
assert iterator.num_results == 0
- assert six.next(items_iter) == item1
+ assert next(items_iter) == item1
assert iterator.num_results == 1
- assert six.next(items_iter) == item2
+ assert next(items_iter) == item2
assert iterator.num_results == 2
- assert six.next(items_iter) == item3
+ assert next(items_iter) == item3
assert iterator.num_results == 3
with pytest.raises(StopIteration):
- six.next(items_iter)
+ next(items_iter)
def test___iter__(self):
iterator = PageIteratorImpl(None, None)
@@ -289,16 +288,16 @@
items_iter = iter(iterator)
- val1 = six.next(items_iter)
+ val1 = next(items_iter)
assert val1 == item1
assert iterator.num_results == 1
- val2 = six.next(items_iter)
+ val2 = next(items_iter)
assert val2 == item2
assert iterator.num_results == 2
with pytest.raises(StopIteration):
- six.next(items_iter)
+ next(items_iter)
api_request.assert_called_once_with(method="GET", path=path, query_params={})
@@ -503,7 +502,7 @@
items_iter = iter(iterator.pages)
npages = int(math.ceil(float(n_results) / page_size))
for ipage in range(npages):
- assert list(six.next(items_iter)) == [
+ assert list(next(items_iter)) == [
dict(name=str(i))
for i in range(
ipage * page_size, min((ipage + 1) * page_size, n_results),
@@ -512,11 +511,11 @@
else:
items_iter = iter(iterator)
for i in range(n_results):
- assert six.next(items_iter) == dict(name=str(i))
+ assert next(items_iter) == dict(name=str(i))
assert iterator.num_results == i + 1
with pytest.raises(StopIteration):
- six.next(items_iter)
+ next(items_iter)
class TestGRPCIterator(object):
@@ -621,7 +620,7 @@
self.page_token = page_token
def next(self):
- return six.next(self._pages)
+ return next(self._pages)
__next__ = next