Skip to content

Commit

Permalink
feat(api core): simplify from_rfc3339 methods (#9641)
Browse files Browse the repository at this point in the history
  • Loading branch information
emar-kar authored Nov 13, 2019
1 parent 56a5554 commit fd93452
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 57 deletions.
10 changes: 5 additions & 5 deletions api_core/google/api_core/bidi.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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)
)


Expand Down Expand Up @@ -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
Expand Down
37 changes: 15 additions & 22 deletions api_core/google/api_core/datetime_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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.
Expand Down Expand Up @@ -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`:
Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion api_core/google/api_core/iam.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
10 changes: 6 additions & 4 deletions api_core/google/api_core/protobuf_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@
"""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

from google.protobuf import field_mask_pb2
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,
Expand Down
2 changes: 1 addition & 1 deletion api_core/google/api_core/retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
88 changes: 64 additions & 24 deletions api_core/tests/unit/test_datetime_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand All @@ -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(
Expand All @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit fd93452

Please sign in to comment.