diff --git a/api_core/google/api_core/bidi.py b/api_core/google/api_core/bidi.py index b171a4112a31..be52d97d42f9 100644 --- a/api_core/google/api_core/bidi.py +++ b/api_core/google/api_core/bidi.py @@ -172,7 +172,9 @@ def __init__(self, access_limit, time_window): self._time_window = time_window self._access_limit = access_limit - self._past_entries = collections.deque(maxlen=access_limit) # least recent first + self._past_entries = collections.deque( + maxlen=access_limit + ) # least recent first self._entry_lock = threading.Lock() def __enter__(self): @@ -198,9 +200,7 @@ def __exit__(self, *_): def __repr__(self): return "{}(access_limit={}, time_window={})".format( - self.__class__.__name__, - self._access_limit, - repr(self._time_window), + self.__class__.__name__, self._access_limit, repr(self._time_window) ) @@ -423,7 +423,7 @@ def __init__( if throttle_reopen: self._reopen_throttle = _Throttle( - access_limit=5, time_window=datetime.timedelta(seconds=10), + access_limit=5, time_window=datetime.timedelta(seconds=10) ) else: self._reopen_throttle = None diff --git a/api_core/google/api_core/datetime_helpers.py b/api_core/google/api_core/datetime_helpers.py index 84c1bb7f512c..e52fb1dd3bcf 100644 --- a/api_core/google/api_core/datetime_helpers.py +++ b/api_core/google/api_core/datetime_helpers.py @@ -115,20 +115,10 @@ def from_iso8601_time(value): def from_rfc3339(value): - """Convert a microsecond-precision timestamp to datetime. + """Convert an RFC3339-format timestamp to a native datetime. - Args: - value (str): The RFC3339 string to convert. - - Returns: - datetime.datetime: The datetime object equivalent to the timestamp in - UTC. - """ - return datetime.datetime.strptime(value, _RFC3339_MICROS).replace(tzinfo=pytz.utc) - - -def from_rfc3339_nanos(value): - """Convert a nanosecond-precision timestamp to a native datetime. + Supported formats include those without fractional seconds, or with + any fraction up to nanosecond precision. .. note:: Python datetimes do not support nanosecond precision; this function @@ -138,11 +128,11 @@ def from_rfc3339_nanos(value): value (str): The RFC3339 string to convert. Returns: - datetime.datetime: The datetime object equivalent to the timestamp in - UTC. + datetime.datetime: The datetime object equivalent to the timestamp + in UTC. Raises: - ValueError: If the timestamp does not match the RFC 3339 + ValueError: If the timestamp does not match the RFC3339 regular expression. """ with_nanos = _RFC3339_NANOS.match(value) @@ -169,6 +159,9 @@ def from_rfc3339_nanos(value): return bare_seconds.replace(microsecond=micros, tzinfo=pytz.utc) +from_rfc3339_nanos = from_rfc3339 # from_rfc3339_nanos method was deprecated. + + def to_rfc3339(value, ignore_zone=True): """Convert a datetime to an RFC3339 timestamp string. @@ -215,22 +208,22 @@ def nanosecond(self): return self._nanosecond def rfc3339(self): - """Return an RFC 3339-compliant timestamp. + """Return an RFC3339-compliant timestamp. Returns: - (str): Timestamp string according to RFC 3339 spec. + (str): Timestamp string according to RFC3339 spec. """ if self._nanosecond == 0: return to_rfc3339(self) - nanos = str(self._nanosecond).rjust(9, '0').rstrip("0") + nanos = str(self._nanosecond).rjust(9, "0").rstrip("0") return "{}.{}Z".format(self.strftime(_RFC3339_NO_FRACTION), nanos) @classmethod def from_rfc3339(cls, stamp): - """Parse RFC 3339-compliant timestamp, preserving nanoseconds. + """Parse RFC3339-compliant timestamp, preserving nanoseconds. Args: - stamp (str): RFC 3339 stamp, with up to nanosecond precision + stamp (str): RFC3339 stamp, with up to nanosecond precision Returns: :class:`DatetimeWithNanoseconds`: @@ -280,7 +273,7 @@ def timestamp_pb(self): @classmethod def from_timestamp_pb(cls, stamp): - """Parse RFC 3339-compliant timestamp, preserving nanoseconds. + """Parse RFC3339-compliant timestamp, preserving nanoseconds. Args: stamp (:class:`~google.protobuf.timestamp_pb2.Timestamp`): timestamp message diff --git a/api_core/google/api_core/iam.py b/api_core/google/api_core/iam.py index 0e108a30679a..04680eb58869 100644 --- a/api_core/google/api_core/iam.py +++ b/api_core/google/api_core/iam.py @@ -34,11 +34,12 @@ """ import collections +import warnings + try: from collections import abc as collections_abc except ImportError: # Python 2.7 import collections as collections_abc -import warnings # Generic IAM roles diff --git a/api_core/google/api_core/protobuf_helpers.py b/api_core/google/api_core/protobuf_helpers.py index aec6b3620743..365ef25c6784 100644 --- a/api_core/google/api_core/protobuf_helpers.py +++ b/api_core/google/api_core/protobuf_helpers.py @@ -15,10 +15,6 @@ """Helpers for :mod:`protobuf`.""" import collections -try: - from collections import abc as collections_abc -except ImportError: # Python 2.7 - import collections as collections_abc import copy import inspect @@ -26,6 +22,12 @@ 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 = ( wrappers_pb2.BoolValue, diff --git a/api_core/google/api_core/retry.py b/api_core/google/api_core/retry.py index 79343bad3471..5962a68b2a87 100644 --- a/api_core/google/api_core/retry.py +++ b/api_core/google/api_core/retry.py @@ -237,7 +237,7 @@ def __init__( maximum=_DEFAULT_MAXIMUM_DELAY, multiplier=_DEFAULT_DELAY_MULTIPLIER, deadline=_DEFAULT_DEADLINE, - on_error=None + on_error=None, ): self._predicate = predicate self._initial = initial diff --git a/api_core/tests/unit/test_datetime_helpers.py b/api_core/tests/unit/test_datetime_helpers.py index 4d138c88a80c..4ddcf36103f2 100644 --- a/api_core/tests/unit/test_datetime_helpers.py +++ b/api_core/tests/unit/test_datetime_helpers.py @@ -82,18 +82,18 @@ def test_from_rfc3339(): ) -def test_from_rfc3339_with_bad_tz(): - value = "2009-12-17T12:44:32.123456BAD" - - with pytest.raises(ValueError): - datetime_helpers.from_rfc3339(value) - +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 + ) -def test_from_rfc3339_with_nanos(): - value = "2009-12-17T12:44:32.123456789Z" - with pytest.raises(ValueError): - datetime_helpers.from_rfc3339(value) +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 + ) def test_from_rfc3339_nanos_without_nanos(): @@ -103,11 +103,33 @@ def test_from_rfc3339_nanos_without_nanos(): ) -def test_from_rfc3339_nanos_with_bad_tz(): - value = "2009-12-17T12:44:32.123456789BAD" +@pytest.mark.parametrize( + "truncated, micros", + [ + ("12345678", 123456), + ("1234567", 123456), + ("123456", 123456), + ("12345", 123450), + ("1234", 123400), + ("123", 123000), + ("12", 120000), + ("1", 100000), + ], +) +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 + ) + + +def test_from_rfc3339_nanos_is_deprecated(): + value = "2009-12-17T12:44:32.123456Z" - with pytest.raises(ValueError): - datetime_helpers.from_rfc3339_nanos(value) + result = datetime_helpers.from_rfc3339(value) + result_nanos = datetime_helpers.from_rfc3339_nanos(value) + + assert result == result_nanos @pytest.mark.parametrize( @@ -130,6 +152,18 @@ def test_from_rfc3339_nanos_with_truncated_nanos(truncated, micros): ) +def test_from_rfc3339_wo_nanos_raise_exception(): + value = "2009-12-17T12:44:32" + with pytest.raises(ValueError): + datetime_helpers.from_rfc3339(value) + + +def test_from_rfc3339_w_nanos_raise_exception(): + value = "2009-12-17T12:44:32.123456" + with pytest.raises(ValueError): + datetime_helpers.from_rfc3339(value) + + def test_to_rfc3339(): value = datetime.datetime(2016, 4, 5, 13, 30, 0) expected = "2016-04-05T13:30:00.000000Z" @@ -157,10 +191,11 @@ def test_to_rfc3339_with_non_utc_ignore_zone(): class Test_DateTimeWithNanos(object): - @staticmethod def test_ctor_wo_nanos(): - stamp = datetime_helpers.DatetimeWithNanoseconds(2016, 12, 20, 21, 13, 47, 123456) + stamp = datetime_helpers.DatetimeWithNanoseconds( + 2016, 12, 20, 21, 13, 47, 123456 + ) assert stamp.year == 2016 assert stamp.month == 12 assert stamp.day == 20 @@ -200,7 +235,9 @@ def test_ctor_w_micros_keyword_and_nanos(): @staticmethod def test_rfc3339_wo_nanos(): - stamp = datetime_helpers.DatetimeWithNanoseconds(2016, 12, 20, 21, 13, 47, 123456) + stamp = datetime_helpers.DatetimeWithNanoseconds( + 2016, 12, 20, 21, 13, 47, 123456 + ) assert stamp.rfc3339() == "2016-12-20T21:13:47.123456Z" @staticmethod @@ -285,12 +322,16 @@ def test_from_rfc3339_w_full_precision(): ) def test_from_rfc3339_test_nanoseconds(fractional, nanos): value = "2009-12-17T12:44:32.{}Z".format(fractional) - assert datetime_helpers.DatetimeWithNanoseconds.from_rfc3339(value).nanosecond == nanos + assert ( + datetime_helpers.DatetimeWithNanoseconds.from_rfc3339(value).nanosecond + == nanos + ) @staticmethod def test_timestamp_pb_wo_nanos_naive(): stamp = datetime_helpers.DatetimeWithNanoseconds( - 2016, 12, 20, 21, 13, 47, 123456) + 2016, 12, 20, 21, 13, 47, 123456 + ) delta = stamp.replace(tzinfo=pytz.UTC) - datetime_helpers._UTC_EPOCH seconds = int(delta.total_seconds()) nanos = 123456000 @@ -304,7 +345,8 @@ def test_timestamp_pb_w_nanos(): ) delta = stamp - datetime_helpers._UTC_EPOCH timestamp = timestamp_pb2.Timestamp( - seconds=int(delta.total_seconds()), nanos=123456789) + seconds=int(delta.total_seconds()), nanos=123456789 + ) assert stamp.timestamp_pb() == timestamp @staticmethod @@ -314,8 +356,7 @@ def test_from_timestamp_pb_wo_nanos(): seconds = int(delta.total_seconds()) timestamp = timestamp_pb2.Timestamp(seconds=seconds) - stamp = datetime_helpers.DatetimeWithNanoseconds.from_timestamp_pb( - timestamp) + stamp = datetime_helpers.DatetimeWithNanoseconds.from_timestamp_pb(timestamp) assert _to_seconds(when) == _to_seconds(stamp) assert stamp.microsecond == 0 @@ -329,8 +370,7 @@ def test_from_timestamp_pb_w_nanos(): seconds = int(delta.total_seconds()) timestamp = timestamp_pb2.Timestamp(seconds=seconds, nanos=123456789) - stamp = datetime_helpers.DatetimeWithNanoseconds.from_timestamp_pb( - timestamp) + stamp = datetime_helpers.DatetimeWithNanoseconds.from_timestamp_pb(timestamp) assert _to_seconds(when) == _to_seconds(stamp) assert stamp.microsecond == 123456