Skip to content

Commit a871ea8

Browse files
committed
Merge pull request #432 from basho/fixes/lrb/gh-430-millisecond-precision
READY: Fix millisecond handling Reviewed-by: javajolt
2 parents 65f72fa + 65fa199 commit a871ea8

File tree

6 files changed

+82
-21
lines changed

6 files changed

+82
-21
lines changed

README.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,15 @@ Install
1717
=======
1818

1919
The recommended versions of Python for use with this client are Python
20-
`2.7.x`, `3.3.x`, `3.4.x` and `3.5.x`.
20+
`2.7.x`, `3.3.x`, `3.4.x` and `3.5.x`. The latest version from each series
21+
should be preferred.
22+
23+
Riak TS (Timeseries)
24+
===================
25+
26+
You must use version `2.7.11`, `3.4.4` or `3.5.1` (or greater within a version series).
27+
Otherwise you will be affected by `this Python bug <https://bugs.python.org/issue23517>`_.
28+
2129

2230
From Source
2331
-----------

RELEASE_NOTES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Riak Python Client Release Notes
22

3+
## 2.4.1 Patch Release - 2016-02-03
4+
5+
* [Riak TS: Millisecond precision](https://github.com/basho/riak-python-client/issues/430)
6+
* [Fix release process](https://github.com/basho/riak-python-client/issues/429)
7+
38
## 2.4.0 Feature Release - 2016-01-13
49

510
This release enhances Riak Time Series functionality.

riak/tests/test_timeseries.py

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
import platform
44
import random
55
import string
6+
67
import riak.pb.riak_ts_pb2
78

89
from riak import RiakError
910
from riak.table import Table
1011
from riak.ts_object import TsObject
1112
from riak.transports.pbc.codec import RiakPbcCodec
12-
from riak.util import str_to_bytes, bytes_to_str
13+
from riak.util import str_to_bytes, bytes_to_str, \
14+
is_timeseries_supported
1315
from riak.tests import RUN_TIMESERIES
1416
from riak.tests.base import IntegrationTestBase
1517
from riak.pb.riak_ts_pb2 import TsColumnType
@@ -25,21 +27,36 @@
2527
bd1 = 'временные ряды'
2628

2729
fiveMins = datetime.timedelta(0, 300)
28-
ts0 = datetime.datetime(2015, 1, 1, 12, 0, 0)
30+
# NB: last arg is microseconds, 987ms expressed
31+
ts0 = datetime.datetime(2015, 1, 1, 12, 0, 0, 987000)
32+
ex0ms = 1420113600987
33+
2934
ts1 = ts0 + fiveMins
35+
ex1ms = 1420113900987
3036

3137

38+
@unittest.skipUnless(is_timeseries_supported(), "Timeseries not supported")
3239
class TimeseriesUnitTests(unittest.TestCase):
33-
def setUp(self):
34-
self.c = RiakPbcCodec()
35-
self.ts0ms = self.c._unix_time_millis(ts0)
36-
self.ts1ms = self.c._unix_time_millis(ts1)
37-
self.rows = [
40+
@classmethod
41+
def setUpClass(cls):
42+
cls.c = RiakPbcCodec()
43+
44+
cls.ts0ms = cls.c._unix_time_millis(ts0)
45+
if cls.ts0ms != ex0ms:
46+
raise AssertionError(
47+
'expected {:d} to equal {:d}'.format(cls.ts0ms, ex0ms))
48+
49+
cls.ts1ms = cls.c._unix_time_millis(ts1)
50+
if cls.ts1ms != ex1ms:
51+
raise AssertionError(
52+
'expected {:d} to equal {:d}'.format(cls.ts1ms, ex1ms))
53+
54+
cls.rows = [
3855
[bd0, 0, 1.2, ts0, True],
3956
[bd1, 3, 4.5, ts1, False]
4057
]
41-
self.test_key = ['hash1', 'user2', ts0]
42-
self.table = Table(None, 'test-table')
58+
cls.test_key = ['hash1', 'user2', ts0]
59+
cls.table = Table(None, 'test-table')
4360

4461
def validate_keyreq(self, req):
4562
self.assertEqual(self.table.name, bytes_to_str(req.table))
@@ -48,6 +65,12 @@ def validate_keyreq(self, req):
4865
self.assertEqual('user2', bytes_to_str(req.key[1].varchar_value))
4966
self.assertEqual(self.ts0ms, req.key[2].timestamp_value)
5067

68+
def test_encode_decode_timestamp(self):
69+
ts0ms = self.c._unix_time_millis(ts0)
70+
self.assertEqual(ts0ms, ex0ms)
71+
ts0_d = self.c._datetime_from_unix_time_millis(ts0ms)
72+
self.assertEqual(ts0, ts0_d)
73+
5174
def test_encode_data_for_get(self):
5275
req = riak.pb.riak_ts_pb2.TsGetReq()
5376
self.c._encode_timeseries_keyreq(self.table, self.test_key, req)
@@ -166,12 +189,13 @@ def test_decode_data_from_query(self):
166189
self.assertEqual(r1[4], self.rows[1][4])
167190

168191

169-
@unittest.skipUnless(RUN_TIMESERIES, 'RUN_TIMESERIES is 0')
192+
@unittest.skipUnless(is_timeseries_supported() and RUN_TIMESERIES,
193+
'Timeseries not supported or RUN_TIMESERIES is 0')
170194
class TimeseriesTests(IntegrationTestBase, unittest.TestCase):
171195
@classmethod
172196
def setUpClass(cls):
173197
super(TimeseriesTests, cls).setUpClass()
174-
cls.now = datetime.datetime.utcfromtimestamp(144379690)
198+
cls.now = datetime.datetime.utcfromtimestamp(144379690.987000)
175199
fiveMinsAgo = cls.now - fiveMins
176200
tenMinsAgo = fiveMinsAgo - fiveMins
177201
fifteenMinsAgo = tenMinsAgo - fiveMins
@@ -211,6 +235,7 @@ def validate_data(self, ts_obj):
211235
self.assertEqual(row[0], 'hash1')
212236
self.assertEqual(row[1], 'user2')
213237
self.assertEqual(row[2], self.fiveMinsAgo)
238+
self.assertEqual(row[2].microsecond, 987000)
214239
self.assertEqual(row[3], 'wind')
215240
self.assertIsNone(row[4])
216241

riak/tests/test_util.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import platform
2+
3+
from riak.util import is_timeseries_supported
4+
5+
if platform.python_version() < '2.7':
6+
unittest = __import__('unittest2')
7+
else:
8+
import unittest
9+
10+
11+
class UtilUnitTests(unittest.TestCase):
12+
def test_is_timeseries_supported(self):
13+
v = (2, 7, 11)
14+
self.assertEqual(True, is_timeseries_supported(v))
15+
v = (2, 7, 12)
16+
self.assertEqual(True, is_timeseries_supported(v))
17+
v = (3, 3, 6)
18+
self.assertEqual(False, is_timeseries_supported(v))
19+
v = (3, 4, 3)
20+
self.assertEqual(False, is_timeseries_supported(v))

riak/transports/pbc/codec.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import datetime
2+
import logging
13
import riak.pb
24
import riak.pb.riak_pb2
35
import riak.pb.riak_dt_pb2
46
import riak.pb.riak_kv_pb2
57
import riak.pb.riak_ts_pb2
6-
import logging
7-
import datetime
88

99
from riak import RiakError
1010
from riak.content import RiakContent
@@ -85,13 +85,7 @@ def __init__(self, **unused_args):
8585

8686
def _unix_time_millis(self, dt):
8787
td = dt - epoch
88-
try:
89-
return int(dt.total_seconds() * 1000.0)
90-
except AttributeError:
91-
# NB: python 2.6 must use this method
92-
return int(((td.microseconds +
93-
(td.seconds + td.days * 24 * 3600) * 10**6) /
94-
10**6) * 1000.0)
88+
return int(td.total_seconds() * 1000.0)
9589

9690
def _datetime_from_unix_time_millis(self, ut):
9791
return datetime.datetime.utcfromtimestamp(ut / 1000.0)

riak/util.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
11
from __future__ import print_function
2+
3+
import sys
24
import warnings
5+
36
from collections import Mapping
47
from six import string_types, PY2
58

69

10+
def is_timeseries_supported(v=None):
11+
if v is None:
12+
v = sys.version_info
13+
return v < (3,) or v >= (3, 4, 4)
14+
15+
716
def quacks_like_dict(object):
817
"""Check if object is dict-like"""
918
return isinstance(object, Mapping)

0 commit comments

Comments
 (0)