FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Remove Timestamp With Nanos from Spanner · googleapis/google-cloud-python@543112a · GitHub

Commit 543112a

Browse files
committed
Remove Timestamp With Nanos from Spanner
1 parent ed3c300 commit 543112a

3 files changed

Lines changed: 49 additions & 196 deletions

File tree

‎spanner/google/cloud/spanner_v1/_helpers.py‎

Lines changed: 7 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -19,80 +19,12 @@
1919

2020
import six
2121

22-
from google.protobuf.struct_pb2 import ListValue
23-
from google.protobuf.struct_pb2 import Value
24-
from google.cloud.spanner_v1.proto import type_pb2
25-
22+
from google.api_core import datetime_helpers
2623
from google.cloud._helpers import _date_from_iso8601_date
2724
from google.cloud._helpers import _datetime_to_rfc3339
28-
from google.cloud._helpers import _RFC3339_NANOS
29-
from google.cloud._helpers import _RFC3339_NO_FRACTION
30-
from google.cloud._helpers import UTC
31-
32-
33-
class TimestampWithNanoseconds(datetime.datetime):
34-
"""Track nanosecond in addition to normal datetime attrs.
35-
36-
nanosecond can be passed only as a keyword argument.
37-
"""
38-
__slots__ = ('_nanosecond',)
39-
40-
# pylint: disable=arguments-differ
41-
def __new__(cls, *args, **kw):
42-
nanos = kw.pop('nanosecond', 0)
43-
if nanos > 0:
44-
if 'microsecond' in kw:
45-
raise TypeError(
46-
"Specify only one of 'microsecond' or 'nanosecond'")
47-
kw['microsecond'] = nanos // 1000
48-
inst = datetime.datetime.__new__(cls, *args, **kw)
49-
inst._nanosecond = nanos or 0
50-
return inst
51-
# pylint: disable=arguments-differ
52-
53-
@property
54-
def nanosecond(self):
55-
"""Read-only: nanosecond precision."""
56-
return self._nanosecond
57-
58-
def rfc3339(self):
59-
"""RFC 3339-compliant timestamp.
60-
61-
:rtype: str
62-
:returns: Timestamp string according to RFC 3339 spec.
63-
"""
64-
if self._nanosecond == 0:
65-
return _datetime_to_rfc3339(self)
66-
nanos = str(self._nanosecond).rstrip('0')
67-
return '%s.%sZ' % (self.strftime(_RFC3339_NO_FRACTION), nanos)
68-
69-
@classmethod
70-
def from_rfc3339(cls, stamp):
71-
"""Parse RFC 3339-compliant timestamp, preserving nanoseconds.
72-
73-
:type stamp: str
74-
:param stamp: RFC 3339 stamp, with up to nanosecond precision
75-
76-
:rtype: :class:`TimestampWithNanoseconds`
77-
:returns: an instance matching the timestamp string
78-
:raises ValueError: if ``stamp`` does not match the expected format
79-
"""
80-
with_nanos = _RFC3339_NANOS.match(stamp)
81-
if with_nanos is None:
82-
raise ValueError(
83-
'Timestamp: %r, does not match pattern: %r' % (
84-
stamp, _RFC3339_NANOS.pattern))
85-
bare = datetime.datetime.strptime(
86-
with_nanos.group('no_fraction'), _RFC3339_NO_FRACTION)
87-
fraction = with_nanos.group('nanos')
88-
if fraction is None:
89-
nanos = 0
90-
else:
91-
scale = 9 - len(fraction)
92-
nanos = int(fraction) * (10 ** scale)
93-
return cls(bare.year, bare.month, bare.day,
94-
bare.hour, bare.minute, bare.second,
95-
nanosecond=nanos, tzinfo=UTC)
25+
from google.cloud.spanner_v1.proto import type_pb2
26+
from google.protobuf.struct_pb2 import ListValue
27+
from google.protobuf.struct_pb2 import Value
9628

9729

9830
def _try_to_coerce_bytes(bytestring):
@@ -140,7 +72,7 @@ def _make_value_pb(value):
14072
else:
14173
return Value(string_value='-Infinity')
14274
return Value(number_value=value)
143-
if isinstance(value, TimestampWithNanoseconds):
75+
if isinstance(value, datetime_helpers.DatetimeWithNanoseconds):
14476
return Value(string_value=value.rfc3339())
14577
if isinstance(value, datetime.datetime):
14678
return Value(string_value=_datetime_to_rfc3339(value))
@@ -211,7 +143,8 @@ def _parse_value_pb(value_pb, field_type):
211143
elif field_type.code == type_pb2.DATE:
212144
result = _date_from_iso8601_date(value_pb.string_value)
213145
elif field_type.code == type_pb2.TIMESTAMP:
214-
result = TimestampWithNanoseconds.from_rfc3339(value_pb.string_value)
146+
DatetimeWithNanoseconds = datetime_helpers.DatetimeWithNanoseconds
147+
result = DatetimeWithNanoseconds.from_rfc3339(value_pb.string_value)
215148
elif field_type.code == type_pb2.ARRAY:
216149
result = [
217150
_parse_value_pb(item_pb, field_type.array_element_type)

‎spanner/tests/system/test_system.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import unittest
2323

2424
from google.api_core import exceptions
25+
from google.api_core.datetime_helpers import DatetimeWithNanoseconds
2526
from google.cloud.spanner_v1.proto.type_pb2 import ARRAY
2627
from google.cloud.spanner_v1.proto.type_pb2 import BOOL
2728
from google.cloud.spanner_v1.proto.type_pb2 import BYTES
@@ -33,7 +34,6 @@
3334
from google.cloud.spanner_v1.proto.type_pb2 import Type
3435

3536
from google.cloud._helpers import UTC
36-
from google.cloud.spanner_v1._helpers import TimestampWithNanoseconds
3737
from google.cloud.spanner import Client
3838
from google.cloud.spanner import KeyRange
3939
from google.cloud.spanner import KeySet
@@ -206,7 +206,7 @@ def _assert_timestamp(self, value, nano_value):
206206
self.assertEqual(value.minute, nano_value.minute)
207207
self.assertEqual(value.second, nano_value.second)
208208
self.assertEqual(value.microsecond, nano_value.microsecond)
209-
if isinstance(value, TimestampWithNanoseconds):
209+
if isinstance(value, DatetimeWithNanoseconds):
210210
self.assertEqual(value.nanosecond, nano_value.nanosecond)
211211
else:
212212
self.assertEqual(value.microsecond * 1000, nano_value.nanosecond)
@@ -222,7 +222,7 @@ def _check_rows_data(self, rows_data, expected=None):
222222
def _check_row_data(self, row_data, expected):
223223
self.assertEqual(len(row_data), len(expected))
224224
for found_cell, expected_cell in zip(row_data, expected):
225-
if isinstance(found_cell, TimestampWithNanoseconds):
225+
if isinstance(found_cell, DatetimeWithNanoseconds):
226226
self._assert_timestamp(expected_cell, found_cell)
227227
elif isinstance(found_cell, float) and math.isnan(found_cell):
228228
self.assertTrue(math.isnan(expected_cell))
@@ -410,7 +410,7 @@ class TestSessionAPI(unittest.TestCase, _TestData):
410410
)
411411
SOME_DATE = datetime.date(2011, 1, 17)
412412
SOME_TIME = datetime.datetime(1989, 1, 17, 17, 59, 12, 345612)
413-
NANO_TIME = TimestampWithNanoseconds(1995, 8, 31, nanosecond=987654321)
413+
NANO_TIME = DatetimeWithNanoseconds(1995, 8, 31, nanosecond=987654321)
414414
OTHER_NAN, = struct.unpack('<d', b'\x01\x00\x01\x00\x00\x00\xf8\xff')
415415
BYTES_1 = b'Ymlu'
416416
BYTES_2 = b'Ym9vdHM='

‎spanner/tests/unit/test__helpers.py‎

Lines changed: 38 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -16,101 +16,6 @@
1616
import unittest
1717

1818

19-
class TestTimestampWithNanoseconds(unittest.TestCase):
20-
21-
def _get_target_class(self):
22-
from google.cloud.spanner_v1._helpers import TimestampWithNanoseconds
23-
24-
return TimestampWithNanoseconds
25-
26-
def _make_one(self, *args, **kw):
27-
return self._get_target_class()(*args, **kw)
28-
29-
def test_ctor_wo_nanos(self):
30-
stamp = self._make_one(2016, 12, 20, 21, 13, 47, 123456)
31-
self.assertEqual(stamp.year, 2016)
32-
self.assertEqual(stamp.month, 12)
33-
self.assertEqual(stamp.day, 20)
34-
self.assertEqual(stamp.hour, 21)
35-
self.assertEqual(stamp.minute, 13)
36-
self.assertEqual(stamp.second, 47)
37-
self.assertEqual(stamp.microsecond, 123456)
38-
self.assertEqual(stamp.nanosecond, 0)
39-
40-
def test_ctor_w_nanos(self):
41-
stamp = self._make_one(
42-
2016, 12, 20, 21, 13, 47, nanosecond=123456789)
43-
self.assertEqual(stamp.year, 2016)
44-
self.assertEqual(stamp.month, 12)
45-
self.assertEqual(stamp.day, 20)
46-
self.assertEqual(stamp.hour, 21)
47-
self.assertEqual(stamp.minute, 13)
48-
self.assertEqual(stamp.second, 47)
49-
self.assertEqual(stamp.microsecond, 123456)
50-
self.assertEqual(stamp.nanosecond, 123456789)
51-
52-
def test_ctor_w_micros_positional_and_nanos(self):
53-
with self.assertRaises(TypeError):
54-
self._make_one(
55-
2016, 12, 20, 21, 13, 47, 123456, nanosecond=123456789)
56-
57-
def test_ctor_w_micros_keyword_and_nanos(self):
58-
with self.assertRaises(TypeError):
59-
self._make_one(
60-
2016, 12, 20, 21, 13, 47,
61-
microsecond=123456, nanosecond=123456789)
62-
63-
def test_rfc339_wo_nanos(self):
64-
stamp = self._make_one(2016, 12, 20, 21, 13, 47, 123456)
65-
self.assertEqual(stamp.rfc3339(),
66-
'2016-12-20T21:13:47.123456Z')
67-
68-
def test_rfc339_w_nanos(self):
69-
stamp = self._make_one(2016, 12, 20, 21, 13, 47, nanosecond=123456789)
70-
self.assertEqual(stamp.rfc3339(),
71-
'2016-12-20T21:13:47.123456789Z')
72-
73-
def test_rfc339_w_nanos_no_trailing_zeroes(self):
74-
stamp = self._make_one(2016, 12, 20, 21, 13, 47, nanosecond=100000000)
75-
self.assertEqual(stamp.rfc3339(),
76-
'2016-12-20T21:13:47.1Z')
77-
78-
def test_from_rfc3339_w_invalid(self):
79-
klass = self._get_target_class()
80-
STAMP = '2016-12-20T21:13:47'
81-
with self.assertRaises(ValueError):
82-
klass.from_rfc3339(STAMP)
83-
84-
def test_from_rfc3339_wo_fraction(self):
85-
from google.cloud._helpers import UTC
86-
87-
klass = self._get_target_class()
88-
STAMP = '2016-12-20T21:13:47Z'
89-
expected = self._make_one(2016, 12, 20, 21, 13, 47, tzinfo=UTC)
90-
stamp = klass.from_rfc3339(STAMP)
91-
self.assertEqual(stamp, expected)
92-
93-
def test_from_rfc3339_w_partial_precision(self):
94-
from google.cloud._helpers import UTC
95-
96-
klass = self._get_target_class()
97-
STAMP = '2016-12-20T21:13:47.1Z'
98-
expected = self._make_one(2016, 12, 20, 21, 13, 47,
99-
microsecond=100000, tzinfo=UTC)
100-
stamp = klass.from_rfc3339(STAMP)
101-
self.assertEqual(stamp, expected)
102-
103-
def test_from_rfc3339_w_full_precision(self):
104-
from google.cloud._helpers import UTC
105-
106-
klass = self._get_target_class()
107-
STAMP = '2016-12-20T21:13:47.123456789Z'
108-
expected = self._make_one(2016, 12, 20, 21, 13, 47,
109-
nanosecond=123456789, tzinfo=UTC)
110-
stamp = klass.from_rfc3339(STAMP)
111-
self.assertEqual(stamp, expected)
112-
113-
11419
class Test_make_value_pb(unittest.TestCase):
11520

11621
def _callFUT(self, *args, **kw):
@@ -209,25 +114,28 @@ def test_w_date(self):
209114
self.assertEqual(value_pb.string_value, today.isoformat())
210115

211116
def test_w_timestamp_w_nanos(self):
117+
import pytz
118+
from google.api_core import datetime_helpers
212119
from google.protobuf.struct_pb2 import Value
213-
from google.cloud._helpers import UTC
214-
from google.cloud.spanner_v1._helpers import TimestampWithNanoseconds
215120

216-
when = TimestampWithNanoseconds(
217-
2016, 12, 20, 21, 13, 47, nanosecond=123456789, tzinfo=UTC)
121+
when = datetime_helpers.DatetimeWithNanoseconds(
122+
2016, 12, 20, 21, 13, 47, nanosecond=123456789, tzinfo=pytz.UTC)
218123
value_pb = self._callFUT(when)
219124
self.assertIsInstance(value_pb, Value)
220125
self.assertEqual(value_pb.string_value, when.rfc3339())
221126

222127
def test_w_datetime(self):
223128
import datetime
129+
import pytz
130+
from google.api_core import datetime_helpers
224131
from google.protobuf.struct_pb2 import Value
225-
from google.cloud._helpers import UTC, _datetime_to_rfc3339
226132

227-
now = datetime.datetime.utcnow().replace(tzinfo=UTC)
133+
now = datetime.datetime.utcnow().replace(tzinfo=pytz.UTC)
228134
value_pb = self._callFUT(now)
229135
self.assertIsInstance(value_pb, Value)
230-
self.assertEqual(value_pb.string_value, _datetime_to_rfc3339(now))
136+
self.assertEqual(
137+
value_pb.string_value,
138+
datetime_helpers.to_rfc3339(now))
231139

232140
def test_w_unknown_type(self):
233141
with self.assertRaises(ValueError):
@@ -382,34 +290,46 @@ def test_w_date(self):
382290
self.assertEqual(self._callFUT(value_pb, field_type), VALUE)
383291

384292
def test_w_timestamp_wo_nanos(self):
293+
import pytz
294+
from google.api_core import datetime_helpers
295+
from google.cloud.spanner_v1.proto.type_pb2 import TIMESTAMP
296+
from google.cloud.spanner_v1.proto.type_pb2 import Type
385297
from google.protobuf.struct_pb2 import Value
386-
from google.cloud.spanner_v1.proto.type_pb2 import Type, TIMESTAMP
387-
from google.cloud._helpers import UTC, _datetime_to_rfc3339
388-
from google.cloud.spanner_v1._helpers import TimestampWithNanoseconds
389298

390-
VALUE = TimestampWithNanoseconds(
391-
2016, 12, 20, 21, 13, 47, microsecond=123456, tzinfo=UTC)
299+
value = datetime_helpers.DatetimeWithNanoseconds(
300+
2016, 12, 20, 21, 13, 47,
301+
microsecond=123456,
302+
tzinfo=pytz.UTC)
392303
field_type = Type(code=TIMESTAMP)
393-
value_pb = Value(string_value=_datetime_to_rfc3339(VALUE))
304+
value_pb = Value(
305+
string_value=datetime_helpers.to_rfc3339(value))
394306

395307
parsed = self._callFUT(value_pb, field_type)
396-
self.assertIsInstance(parsed, TimestampWithNanoseconds)
397-
self.assertEqual(parsed, VALUE)
308+
self.assertIsInstance(
309+
parsed,
310+
datetime_helpers.DatetimeWithNanoseconds)
311+
self.assertEqual(parsed, value)
398312

399313
def test_w_timestamp_w_nanos(self):
314+
import pytz
315+
from google.api_core import datetime_helpers
316+
from google.cloud.spanner_v1.proto.type_pb2 import TIMESTAMP
317+
from google.cloud.spanner_v1.proto.type_pb2 import Type
400318
from google.protobuf.struct_pb2 import Value
401-
from google.cloud.spanner_v1.proto.type_pb2 import Type, TIMESTAMP
402-
from google.cloud._helpers import UTC, _datetime_to_rfc3339
403-
from google.cloud.spanner_v1._helpers import TimestampWithNanoseconds
404319

405-
VALUE = TimestampWithNanoseconds(
406-
2016, 12, 20, 21, 13, 47, nanosecond=123456789, tzinfo=UTC)
320+
value = datetime_helpers.DatetimeWithNanoseconds(
321+
2016, 12, 20, 21, 13, 47,
322+
nanosecond=123456789,
323+
tzinfo=pytz.UTC)
407324
field_type = Type(code=TIMESTAMP)
408-
value_pb = Value(string_value=_datetime_to_rfc3339(VALUE))
325+
value_pb = Value(
326+
string_value=datetime_helpers.to_rfc3339(value))
409327

410328
parsed = self._callFUT(value_pb, field_type)
411-
self.assertIsInstance(parsed, TimestampWithNanoseconds)
412-
self.assertEqual(parsed, VALUE)
329+
self.assertIsInstance(
330+
parsed,
331+
datetime_helpers.DatetimeWithNanoseconds)
332+
self.assertEqual(parsed, value)
413333

414334
def test_w_array_empty(self):
415335
from google.protobuf.struct_pb2 import Value

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL