From 6f757e8b741b79257ec83c58185a0252c9f7e940 Mon Sep 17 00:00:00 2001 From: aviau Date: Wed, 1 Apr 2015 10:58:15 -0400 Subject: [PATCH 01/26] Query now returns a ResultSet --- influxdb/client.py | 9 +++--- influxdb/resultset.py | 53 ++++++++++++++++++++++++++++++++ tests/influxdb/resultset_test.py | 51 ++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 influxdb/resultset.py create mode 100644 tests/influxdb/resultset_test.py diff --git a/influxdb/client.py b/influxdb/client.py index 4941ebeb..668be519 100755 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -8,6 +8,7 @@ import requests import requests.exceptions +from influxdb.resultset import ResultSet try: xrange @@ -132,10 +133,8 @@ def format_query_response(response): for row in result['series']: items = [] if 'name' in row: - name = row['name'] - tags = row.get('tags', None) - if tags: - name = (row['name'], tuple(tags.items())) + tags = row.get('tags', {}) + name = (row['name'], tuple(tags.items())) assert name not in series series[name] = items else: @@ -147,7 +146,7 @@ def format_query_response(response): for cur_col, field in enumerate(value): item[columns[cur_col]] = field items.append(item) - return series + return ResultSet(series) def switch_database(self, database): """ diff --git a/influxdb/resultset.py b/influxdb/resultset.py new file mode 100644 index 00000000..ca3712d3 --- /dev/null +++ b/influxdb/resultset.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +import collections + + +class ResultSet(collections.MutableMapping): + """A dictionary that applies an arbitrary key-altering + function before accessing the keys""" + + def __init__(self, series): + self.store = dict() + self.update(series) # use the free update to set keys + + def __getitem__(self, key): + if isinstance(key, tuple): + name = key[0] + tags = key[1] + else: + name = key + tags = None + + for serie in self.store.keys(): + if serie[0] == name: + serie_matches = True + serie_tags = dict((tag, value) for tag, value in serie[1]) + if tags is not None: + for tag in tags.items(): + try: + if serie_tags[tag[0]] != tag[1]: + serie_matches = False + break + except KeyError: + serie_matches = False + break + if serie_matches: + yield {"points": self.store[serie], "tags": serie_tags} + + def __setitem__(self, key, value): + self.store[key] = value + + def __repr__(self): + rep = "" + for serie in self.store.keys(): + rep += "%s: %s" % (dict((tag, value) for tag, value in serie[1]), self.store[serie]) + return '%s(%s)' % (type(self).__name__, rep) + + def __delitem__(self, key): + del self.store[key] + + def __iter__(self): + return iter(self.store) + + def __len__(self): + return len(self.store) diff --git a/tests/influxdb/resultset_test.py b/tests/influxdb/resultset_test.py new file mode 100644 index 00000000..0b71af5c --- /dev/null +++ b/tests/influxdb/resultset_test.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- + +import unittest + +from influxdb.resultset import ResultSet + + +class TestResultSet(unittest.TestCase): + + def setUp(self): + self.query_response = { + ('serie', (('tag_1', ''),)): [ + {'time': '2015-03-30T16:16:37Z', 'value': 15}], + ('serie', (('tag_1', 'value1'),)): [ + {'time': '2015-03-30T16:16:37Z', 'value': 5}], + ('serie', (('tag_1', 'value2'),)): [ + {'time': '2015-03-30T16:16:37Z', 'value': 10}] + } + self.rs = ResultSet(self.query_response) + + def test_filter_by_name(self): + self.assertItemsEqual( + self.rs['serie'], + [ + {'points': [{'value': 10, 'time': '2015-03-30T16:16:37Z'}], + 'tags': {'tag_1': 'value2'}}, + {'points': [{'value': 15, 'time': '2015-03-30T16:16:37Z'}], + 'tags': {'tag_1': ''}}, + {'points': [{'value': 5, 'time': '2015-03-30T16:16:37Z'}], + 'tags': {'tag_1': 'value1'}} + ] + ) + + def test_filter_by_tags(self): + self.assertItemsEqual( + self.rs[('serie', {'tag_1': 'value2'})], + [{'points': [{'value': 10, 'time': '2015-03-30T16:16:37Z'}], + 'tags': {'tag_1': 'value2'}}] + ) + + def test_repr(self): + expected = \ + "ResultSet({'tag_1': 'value2'}: [{'value': 10, 'time': " \ + "'2015-03-30T16:16:37Z'}]{'tag_1': ''}: [{'value': 15, 'time': " \ + "'2015-03-30T16:16:37Z'}]{'tag_1': 'value1'}: " \ + "[{'value': 5, 'time': '2015-03-30T16:16:37Z'}])" + + self.assertEqual( + str(self.rs), + expected + ) From a4078c4253b9a07a9babb2ad81853ee5b7a637cf Mon Sep 17 00:00:00 2001 From: aviau Date: Wed, 1 Apr 2015 12:31:19 -0400 Subject: [PATCH 02/26] Fixed repr to include tuple --- influxdb/resultset.py | 2 +- tests/influxdb/resultset_test.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/influxdb/resultset.py b/influxdb/resultset.py index ca3712d3..54fd3e63 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -40,7 +40,7 @@ def __setitem__(self, key, value): def __repr__(self): rep = "" for serie in self.store.keys(): - rep += "%s: %s" % (dict((tag, value) for tag, value in serie[1]), self.store[serie]) + rep += "('%s', %s): %s" % (serie[0], dict((tag, value) for tag, value in serie[1]), self.store[serie]) return '%s(%s)' % (type(self).__name__, rep) def __delitem__(self, key): diff --git a/tests/influxdb/resultset_test.py b/tests/influxdb/resultset_test.py index 0b71af5c..35b49cd1 100644 --- a/tests/influxdb/resultset_test.py +++ b/tests/influxdb/resultset_test.py @@ -40,10 +40,11 @@ def test_filter_by_tags(self): def test_repr(self): expected = \ - "ResultSet({'tag_1': 'value2'}: [{'value': 10, 'time': " \ - "'2015-03-30T16:16:37Z'}]{'tag_1': ''}: [{'value': 15, 'time': " \ - "'2015-03-30T16:16:37Z'}]{'tag_1': 'value1'}: " \ - "[{'value': 5, 'time': '2015-03-30T16:16:37Z'}])" + "ResultSet(('serie', {'tag_1': 'value2'}): [{'value': 10, " \ + "'time': '2015-03-30T16:16:37Z'}]('serie', {'tag_1': ''}):" \ + " [{'value': 15, 'time': '2015-03-30T16:16:37Z'}]('serie'," \ + " {'tag_1': 'value1'}): [{'value': 5," \ + " 'time': '2015-03-30T16:16:37Z'}])" self.assertEqual( str(self.rs), From d6ea8329eda28a0ff05601ec21d97d3666731cd2 Mon Sep 17 00:00:00 2001 From: aviau Date: Wed, 1 Apr 2015 16:26:10 -0400 Subject: [PATCH 03/26] Refactored resultset --- influxdb/client.py | 45 ++++++----------- influxdb/resultset.py | 51 ++++++++++--------- tests/influxdb/client_test.py | 8 ++- tests/influxdb/resultset_test.py | 84 ++++++++++++++++++++++++++------ 4 files changed, 117 insertions(+), 71 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index 668be519..2e44a95c 100755 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -123,31 +123,6 @@ def keep_json_response_order(self): def keep_json_response_order(self, new_value): self._keep_json_response_order = new_value - @staticmethod - def format_query_response(response): - """Returns a list of items from a query response""" - series = {} - if 'results' in response: - for result in response['results']: - if 'series' in result: - for row in result['series']: - items = [] - if 'name' in row: - tags = row.get('tags', {}) - name = (row['name'], tuple(tags.items())) - assert name not in series - series[name] = items - else: - series = items # Special case for system queries. - if 'columns' in row and 'values' in row: - columns = row['columns'] - for value in row['values']: - item = {} - for cur_col, field in enumerate(value): - item[columns[cur_col]] = field - items.append(item) - return ResultSet(series) - def switch_database(self, database): """ switch_database() @@ -233,8 +208,7 @@ def query(self, query, params={}, expected_response_code=200, - database=None, - raw=False): + database=None): """ Query data @@ -260,8 +234,7 @@ def query(self, json_kw.update(object_pairs_hook=OrderedDict) data = response.json(**json_kw) - return (data if raw - else self.format_query_response(data)) + return ResultSet(data) def write_points(self, points, @@ -327,7 +300,8 @@ def get_list_database(self): Get the list of databases """ rsp = self.query("SHOW DATABASES") - return [db['name'] for db in rsp['databases']] + print rsp + return [db['name'] for db in list(rsp[('databases', {})])[0]['points']] def create_database(self, dbname): """ @@ -375,7 +349,16 @@ def get_list_series(self, database=None): """ Get the list of series """ - return self.query("SHOW SERIES", database=database) + + rs = self.query("SHOW SERIES", database=database) + series = list(rs) + result = [] + for serie in rs.keys(): + print "EEEEE", serie + + #x = series[0] + #return x['points'] + return list(self.query("SHOW SERIES", database=database))[0]['points'] def get_list_users(self): """ diff --git a/influxdb/resultset.py b/influxdb/resultset.py index 54fd3e63..c783836e 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -7,7 +7,7 @@ class ResultSet(collections.MutableMapping): function before accessing the keys""" def __init__(self, series): - self.store = dict() + self.raw = dict() self.update(series) # use the free update to set keys def __getitem__(self, key): @@ -18,36 +18,43 @@ def __getitem__(self, key): name = key tags = None - for serie in self.store.keys(): - if serie[0] == name: - serie_matches = True - serie_tags = dict((tag, value) for tag, value in serie[1]) - if tags is not None: - for tag in tags.items(): - try: - if serie_tags[tag[0]] != tag[1]: + results = [] + + for result in self.raw['results']: + for serie in result['series']: + if serie['name'] == name: + serie_matches = True + + if tags is not None: + for tag in tags.items(): + if serie['tags'][tag[0]] != tag[1]: serie_matches = False break - except KeyError: - serie_matches = False - break - if serie_matches: - yield {"points": self.store[serie], "tags": serie_tags} + + if serie_matches: + items = [] + for value in serie['values']: + item = {} + for cur_col, field in enumerate(value): + item[serie['columns'][cur_col]] = field + items.append(item) + + results.append({"points": items, "tags": serie['tags']}) + continue + return results def __setitem__(self, key, value): - self.store[key] = value + self.raw[key] = value def __repr__(self): - rep = "" - for serie in self.store.keys(): - rep += "('%s', %s): %s" % (serie[0], dict((tag, value) for tag, value in serie[1]), self.store[serie]) - return '%s(%s)' % (type(self).__name__, rep) + return str(self.raw) def __delitem__(self, key): - del self.store[key] + del self.raw[key] def __iter__(self): - return iter(self.store) + for serie in self.raw: + yield {"points": self.raw[serie], "tags": dict((tag, value) for tag, value in serie[1])} def __len__(self): - return len(self.store) + return len(self.raw) diff --git a/tests/influxdb/client_test.py b/tests/influxdb/client_test.py index 436e5aad..54a66ef1 100644 --- a/tests/influxdb/client_test.py +++ b/tests/influxdb/client_test.py @@ -367,8 +367,9 @@ def test_get_list_database_fails(self): def test_get_list_series(self): example_response = \ - '{"results": [{"series": [{"values": [["fsfdsdf", "24h0m0s", 2]],'\ - ' "columns": ["name", "duration", "replicaN"]}]}]}' + '{"results": [{"series": [{"name": "cpu_load_short", "columns": ' \ + '["_id", "host", "region"], "values": ' \ + '[[1, "server01", "us-west"]]}]}]}' with requests_mock.Mocker() as m: m.register_uri( @@ -376,6 +377,9 @@ def test_get_list_series(self): "http://localhost:8086/query", text=example_response ) + + print self.cli.get_list_series() + self.assertListEqual( self.cli.get_list_series(), [{'duration': '24h0m0s', diff --git a/tests/influxdb/resultset_test.py b/tests/influxdb/resultset_test.py index 35b49cd1..6bf37915 100644 --- a/tests/influxdb/resultset_test.py +++ b/tests/influxdb/resultset_test.py @@ -9,33 +9,85 @@ class TestResultSet(unittest.TestCase): def setUp(self): self.query_response = { - ('serie', (('tag_1', ''),)): [ - {'time': '2015-03-30T16:16:37Z', 'value': 15}], - ('serie', (('tag_1', 'value1'),)): [ - {'time': '2015-03-30T16:16:37Z', 'value': 5}], - ('serie', (('tag_1', 'value2'),)): [ - {'time': '2015-03-30T16:16:37Z', 'value': 10}] + "results": [ + {"series": [{"name": "cpu_load_short", + "tags": {"host": "server01", + "region": "us-west"}, + "columns": ["time", "value"], + "values": [ + ["2015-01-29T21:51:28.968422294Z", 0.64] + ]}, + {"name": "cpu_load_short", + "tags": {"host": "server02", + "region": "us-west"}, + "columns": ["time", "value"], + "values": [ + ["2015-01-29T21:51:28.968422294Z", 0.64] + ]}, + {"name": "other_serie", + "tags": {"host": "server01", + "region": "us-west"}, + "columns": ["time", "value"], + "values": [ + ["2015-01-29T21:51:28.968422294Z", 0.64] + ]}]} + ] } self.rs = ResultSet(self.query_response) def test_filter_by_name(self): self.assertItemsEqual( - self.rs['serie'], + self.rs['cpu_load_short'], [ - {'points': [{'value': 10, 'time': '2015-03-30T16:16:37Z'}], - 'tags': {'tag_1': 'value2'}}, - {'points': [{'value': 15, 'time': '2015-03-30T16:16:37Z'}], - 'tags': {'tag_1': ''}}, - {'points': [{'value': 5, 'time': '2015-03-30T16:16:37Z'}], - 'tags': {'tag_1': 'value1'}} + { + "tags": {"host": "server01", "region": "us-west"}, + "points": [ + {"time": "2015-01-29T21:51:28.968422294Z", + "value": 0.64} + ] + }, + { + "tags": {"host": "server02", "region": "us-west"}, + "points": [ + {"time": "2015-01-29T21:51:28.968422294Z", + "value": 0.64} + ] + } ] ) def test_filter_by_tags(self): self.assertItemsEqual( - self.rs[('serie', {'tag_1': 'value2'})], - [{'points': [{'value': 10, 'time': '2015-03-30T16:16:37Z'}], - 'tags': {'tag_1': 'value2'}}] + self.rs[('cpu_load_short', {"host": "server01"})], + [ + { + "tags": {"host": "server01", "region": "us-west"}, + "points": [ + {"time": "2015-01-29T21:51:28.968422294Z", + "value": 0.64} + ] + } + ] + ) + + self.assertItemsEqual( + self.rs[('cpu_load_short', {"region": "us-west"})], + [ + { + "tags": {"host": "server01", "region": "us-west"}, + "points": [ + {"time": "2015-01-29T21:51:28.968422294Z", + "value": 0.64} + ] + }, + { + "tags": {"host": "server02", "region": "us-west"}, + "points": [ + {"time": "2015-01-29T21:51:28.968422294Z", + "value": 0.64} + ] + } + ] ) def test_repr(self): From ba2b16891b357ecbc08ba23508a5353d67834300 Mon Sep 17 00:00:00 2001 From: aviau Date: Wed, 1 Apr 2015 18:05:40 -0400 Subject: [PATCH 04/26] begin working on tests --- influxdb/client.py | 20 ++++++-------------- influxdb/resultset.py | 19 +++++++++++++------ tests/influxdb/client_test.py | 2 -- tests/influxdb/client_test_with_server.py | 6 +++--- tests/influxdb/resultset_test.py | 12 ------------ 5 files changed, 22 insertions(+), 37 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index 2e44a95c..60b8e1bb 100755 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -299,9 +299,7 @@ def get_list_database(self): """ Get the list of databases """ - rsp = self.query("SHOW DATABASES") - print rsp - return [db['name'] for db in list(rsp[('databases', {})])[0]['points']] + return self.query("SHOW DATABASES")['results'][0]['points'] def create_database(self, dbname): """ @@ -343,22 +341,16 @@ def get_list_retention_policies(self, database=None): """ return self.query( "SHOW RETENTION POLICIES %s" % (database or self._database) - ) + )['results'][0]['points'] def get_list_series(self, database=None): """ Get the list of series """ - - rs = self.query("SHOW SERIES", database=database) - series = list(rs) - result = [] - for serie in rs.keys(): - print "EEEEE", serie - - #x = series[0] - #return x['points'] - return list(self.query("SHOW SERIES", database=database))[0]['points'] + rsp = self.query("SHOW SERIES", database=database) + print "RSP", rsp.raw + print "RSP", rsp['results'] + return rsp def get_list_users(self): """ diff --git a/influxdb/resultset.py b/influxdb/resultset.py index c783836e..6631cc8c 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -22,24 +22,32 @@ def __getitem__(self, key): for result in self.raw['results']: for serie in result['series']: - if serie['name'] == name: + serie_name = serie.get('name', 'results') + if serie_name == name or serie_name == 'results': serie_matches = True + serie_tags = serie.get('tags', {}) + if tags is not None: for tag in tags.items(): - if serie['tags'][tag[0]] != tag[1]: + try: + if serie_tags[tag[0]] != tag[1]: + serie_matches = False + break + except KeyError: serie_matches = False break if serie_matches: items = [] - for value in serie['values']: + serie_values = serie.get('values', []) + for value in serie_values: item = {} for cur_col, field in enumerate(value): item[serie['columns'][cur_col]] = field items.append(item) - results.append({"points": items, "tags": serie['tags']}) + results.append({"points": items, "tags": serie_tags}) continue return results @@ -53,8 +61,7 @@ def __delitem__(self, key): del self.raw[key] def __iter__(self): - for serie in self.raw: - yield {"points": self.raw[serie], "tags": dict((tag, value) for tag, value in serie[1])} + return iter(self.raw) def __len__(self): return len(self.raw) diff --git a/tests/influxdb/client_test.py b/tests/influxdb/client_test.py index 54a66ef1..5dc03e3d 100644 --- a/tests/influxdb/client_test.py +++ b/tests/influxdb/client_test.py @@ -378,8 +378,6 @@ def test_get_list_series(self): text=example_response ) - print self.cli.get_list_series() - self.assertListEqual( self.cli.get_list_series(), [{'duration': '24h0m0s', diff --git a/tests/influxdb/client_test_with_server.py b/tests/influxdb/client_test_with_server.py index 968d234b..4ffef3f2 100644 --- a/tests/influxdb/client_test_with_server.py +++ b/tests/influxdb/client_test_with_server.py @@ -289,7 +289,7 @@ def test_create_database(self): self.assertIsNone(self.cli.create_database('new_db_2')) self.assertEqual( self.cli.get_list_database(), - ['new_db_1', 'new_db_2'] + [{u'name': u'new_db_1'}, {u'name': u'new_db_2'}] ) def test_create_database_fails(self): @@ -303,7 +303,7 @@ def test_create_database_fails(self): def test_drop_database(self): self.test_create_database() self.assertIsNone(self.cli.drop_database('new_db_1')) - self.assertEqual(['new_db_2'], self.cli.get_list_database()) + self.assertEqual([{u'name': u'new_db_2'}], self.cli.get_list_database()) def test_drop_database_fails(self): with self.assertRaises(InfluxDBClientError) as ctx: @@ -498,7 +498,7 @@ def test_query_chunked(self): def test_get_list_series_empty(self): rsp = self.cli.get_list_series() - self.assertEqual({}, rsp) + self.assertEqual([], rsp) def test_get_list_series_non_empty(self): self.cli.write_points(dummy_point) diff --git a/tests/influxdb/resultset_test.py b/tests/influxdb/resultset_test.py index 6bf37915..8f1768e8 100644 --- a/tests/influxdb/resultset_test.py +++ b/tests/influxdb/resultset_test.py @@ -90,15 +90,3 @@ def test_filter_by_tags(self): ] ) - def test_repr(self): - expected = \ - "ResultSet(('serie', {'tag_1': 'value2'}): [{'value': 10, " \ - "'time': '2015-03-30T16:16:37Z'}]('serie', {'tag_1': ''}):" \ - " [{'value': 15, 'time': '2015-03-30T16:16:37Z'}]('serie'," \ - " {'tag_1': 'value1'}): [{'value': 5," \ - " 'time': '2015-03-30T16:16:37Z'}])" - - self.assertEqual( - str(self.rs), - expected - ) From 3da7d94ec84dcb704196146ab7bf0ecfcc73fcde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Thu, 2 Apr 2015 17:59:02 -0400 Subject: [PATCH 05/26] after rethink about it : not completly sure we really need it.. --- influxdb/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/influxdb/client.py b/influxdb/client.py index 60b8e1bb..e236057f 100755 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -110,7 +110,7 @@ def __init__(self, # if one doesn't care in that, then it can simply change its client # instance 'keep_json_response_order' attribute value (to a falsy one). # This will then eventually help for performance considerations. - _keep_json_response_order = True + _keep_json_response_order = False # NB: For "group by" query type : # This setting is actually necessary in order to have a consistent and # reproducible rsp format if you "group by" on more than 1 tag. From 450e118b71387b174b971652cc841f4e3df961ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Thu, 2 Apr 2015 18:01:54 -0400 Subject: [PATCH 06/26] README ! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit list(resultset) -> liste de série (chacune contenant liste de points) list(resultset["key"]) -> liste de points (correspondant à "key") --- influxdb/resultset.py | 143 ++++++++++++++++++++++++++++++------------ 1 file changed, 104 insertions(+), 39 deletions(-) diff --git a/influxdb/resultset.py b/influxdb/resultset.py index 6631cc8c..1e4dc650 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -1,67 +1,132 @@ # -*- coding: utf-8 -*- + import collections +from collections import namedtuple + + +_sentinel = object() + + +class NamedValues(object): + def __init__(self, point): + self._point = point + + def __getattr__(self, item): + try: + index = self._point.columns.index(item) + except ValueError: + raise AttributeError('Point have no such attribute (%r)' % item) + return self._point._point_values[index] + + def __repr__(self): + return 'Values(%s)' % ', '.join( + '%s=%r' % (k, self._point._point_values[k]) + for k in self._point.columns) + + +class Point(object): + + def __init__(self, serie, columns, values, tags=None): + assert len(columns) == len(values) + self.columns = columns + self._point_values = values + if tags is None: + tags = {} + self.serie = serie + self.tags = tags + self.values = NamedValues(self) + def __getitem__(self, tag_name): + """Indexing a Point return the tag value associated with + the given tag name""" + return self._tags[tag_name] -class ResultSet(collections.MutableMapping): - """A dictionary that applies an arbitrary key-altering - function before accessing the keys""" + def __iter__(self): + """Iterating over a Point will return its tags names one per one""" + return iter(self._tags) + + def __len__(self): + """The len of a Point is its number of columns/values""" + return len(self.columns) + + def __repr__(self): + return 'Point(values=%s, tags=%s)' % ( + ', '.join('%s=%r' % ( + k, getattr(self.values, k)) for k in self.columns), + self.tags) + + + +class ResultSet(object): + """A wrapper around series results """ def __init__(self, series): - self.raw = dict() - self.update(series) # use the free update to set keys + self.raw = series # ['results'] + results = series['results'] + if False: + self.have_tags = ( + results + and 'series' in results[0] + and results[0]['series'] + and 'tags' in results[0]['series'][0] + ) + # self.raw.update(series) # use the free update to set keys def __getitem__(self, key): + ''' + :param key: Either a serie name or a 2-tuple(serie_name, tags_dict) + If the given serie name is None then any serie (matching the eventual + given tags) will be given its points one after the other. + :return: A generator yielding `Point`s matching the given key + ''' if isinstance(key, tuple): + if 2 != len(key): + raise TypeError('only 2-tuples allowed') name = key[0] tags = key[1] + if not isinstance(tags, dict): + raise TypeError('should be a dict') else: name = key tags = None - results = [] - for result in self.raw['results']: for serie in result['series']: - serie_name = serie.get('name', 'results') - if serie_name == name or serie_name == 'results': - serie_matches = True + serie_name = serie.get('name', None) + if serie_name is None: + # this is a "system" query or a query which doesn't returned a named "serie" + # 'list retention' is in this case.. + if key is None: + for point in serie['values']: + yield Point('$SYSTEM$', serie['columns'], point) + elif name in (None, serie_name): + # by default if no tags was provided then + # we will matches every returned serie + serie_matches = True serie_tags = serie.get('tags', {}) - - if tags is not None: - for tag in tags.items(): - try: - if serie_tags[tag[0]] != tag[1]: - serie_matches = False - break - except KeyError: - serie_matches = False + if tags: + serie_matches = False + for tag_name, tag_value in tags.items(): + serie_tag_value = serie_tags.get(tag_name, _sentinel) + if serie_tag_value != tag_value: break + else: + serie_matches = True if serie_matches: - items = [] - serie_values = serie.get('values', []) - for value in serie_values: - item = {} - for cur_col, field in enumerate(value): - item[serie['columns'][cur_col]] = field - items.append(item) - - results.append({"points": items, "tags": serie_tags}) - continue - return results - - def __setitem__(self, key, value): - self.raw[key] = value + for point in serie['values']: + yield Point(serie_name, serie['columns'], point, serie_tags) def __repr__(self): return str(self.raw) - def __delitem__(self, key): - del self.raw[key] - def __iter__(self): - return iter(self.raw) + ''' Iterating a ResultSet will yield one dict instance per serie result. + ''' + for results in self.raw['results']: + for many_series in results['series']: + yield many_series - def __len__(self): - return len(self.raw) + #def __len__(self): + # return len(self.raw) From 1a4b4ebf0c8a4f35553ab8ae64915d7ae59d48e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Thu, 2 Apr 2015 18:02:24 -0400 Subject: [PATCH 07/26] adapted get_list_database().. --- influxdb/client.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index e236057f..989405f7 100755 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -215,7 +215,9 @@ def query(self, :param params: Additional parameters to be passed to requests. :param database: Database to query, default to None. :param expected_response_code: Expected response code. Defaults to 200. - :param raw: Wether or not to return the raw influxdb response. + + :rtype : ResultSet + """ params['q'] = query @@ -299,7 +301,10 @@ def get_list_database(self): """ Get the list of databases """ - return self.query("SHOW DATABASES")['results'][0]['points'] + rsp = self.query("SHOW DATABASES") #['results'][0]['points'] + lrsp = list(rsp) + return [value[0] for value in lrsp[0].get('values', [])] + #return rsp['results'][0]['points'] def create_database(self, dbname): """ From 37be5b2f476ed17c1e31e2edc3068f808c460ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Thu, 2 Apr 2015 18:03:30 -0400 Subject: [PATCH 08/26] =?UTF-8?q?r=C3=A9-adapt=C3=A9=20pour=20get=5Flist?= =?UTF-8?q?=5Fdatabase()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/influxdb/client_test_with_server.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/influxdb/client_test_with_server.py b/tests/influxdb/client_test_with_server.py index 4ffef3f2..43797d2d 100644 --- a/tests/influxdb/client_test_with_server.py +++ b/tests/influxdb/client_test_with_server.py @@ -287,9 +287,10 @@ def test_fresh_server_no_db(self): def test_create_database(self): self.assertIsNone(self.cli.create_database('new_db_1')) self.assertIsNone(self.cli.create_database('new_db_2')) + rsp = self.cli.get_list_database() self.assertEqual( - self.cli.get_list_database(), - [{u'name': u'new_db_1'}, {u'name': u'new_db_2'}] + rsp, + ['new_db_1', 'new_db_2'] ) def test_create_database_fails(self): @@ -303,7 +304,7 @@ def test_create_database_fails(self): def test_drop_database(self): self.test_create_database() self.assertIsNone(self.cli.drop_database('new_db_1')) - self.assertEqual([{u'name': u'new_db_2'}], self.cli.get_list_database()) + self.assertEqual(['new_db_2'], self.cli.get_list_database()) def test_drop_database_fails(self): with self.assertRaises(InfluxDBClientError) as ctx: From a2173902fe8ea53b874e11b129df564bd61df927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Fri, 3 Apr 2015 09:25:48 -0400 Subject: [PATCH 09/26] =?UTF-8?q?adapt=C3=A9:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit + test_write_points_check_read + test_write_multiple_points_different_series + test_create_retention_policy_default --- tests/influxdb/client_test_with_server.py | 140 ++++++++++++++++------ 1 file changed, 102 insertions(+), 38 deletions(-) diff --git a/tests/influxdb/client_test_with_server.py b/tests/influxdb/client_test_with_server.py index 43797d2d..a54a3125 100644 --- a/tests/influxdb/client_test_with_server.py +++ b/tests/influxdb/client_test_with_server.py @@ -12,7 +12,7 @@ from __future__ import print_function import random - +from collections import OrderedDict import datetime import distutils.spawn from functools import partial @@ -360,22 +360,40 @@ def test_write_points_check_read(self): ''' same as test_write_check_read() but with write_points \o/ ''' self.test_write_points() time.sleep(1) # same as test_write_check_read() + rsp = self.cli.query('SELECT * FROM cpu_load_short') self.assertEqual( - {'cpu_load_short': [ - {'value': 0.64, 'time': '2009-11-10T23:00:00Z'}]}, - self.cli.query('SELECT * FROM cpu_load_short')) + [{'values': [['2009-11-10T23:00:00Z', 0.64]], + 'name': 'cpu_load_short', + 'columns': ['time', 'value']}], + list(rsp) + ) + + rsp2 = list(rsp['cpu_load_short']) + self.assertEqual(len(rsp2), 1) + pt = rsp2[0] + + self.assertEqual( + ['cpu_load_short', ['time', 'value'], {}, ['2009-11-10T23:00:00Z', 0.64]], + [pt.serie, pt.columns, pt.tags, [pt.values.time, pt.values.value]] + ) def test_write_multiple_points_different_series(self): self.assertIs(True, self.cli.write_points(dummy_points)) time.sleep(1) + rsp = self.cli.query('SELECT * FROM cpu_load_short') + lrsp = list(rsp) self.assertEqual( - {'cpu_load_short': [ - {'value': 0.64, 'time': '2009-11-10T23:00:00Z'}]}, - self.cli.query('SELECT * FROM cpu_load_short')) + [{'values': [['2009-11-10T23:00:00Z', 0.64]], + 'name': 'cpu_load_short', + 'columns': ['time', 'value']}], + lrsp) + rsp = list(self.cli.query('SELECT * FROM memory')) self.assertEqual( - {'memory': [ - {'time': '2009-11-10T23:01:35Z', 'value': 33}]}, - self.cli.query('SELECT * FROM memory')) + [{ + 'values': [['2009-11-10T23:01:35Z', 33]], + 'name': 'memory', 'columns': ['time', 'value']}], + rsp + ) @unittest.skip('Not implemented for 0.9') def test_write_points_batch(self): @@ -465,7 +483,7 @@ def test_write_points_with_precision(self): time.sleep(sleep_time) rsp = self.cli.query('SELECT * FROM cpu_load_short', database=db) - + rsp = list(rsp)[0] # sys.stderr.write('precision=%s rsp_timestamp = %r\n' % ( # precision, rsp['cpu_load_short'][0]['time'])) m = re.match(expected_regex, rsp['cpu_load_short'][0]['time']) @@ -524,13 +542,10 @@ def test_create_retention_policy_default(self): default=True) self.assertIsNone(rsp) rsp = self.cli.get_list_retention_policies() - self.assertEqual( - [ - {'duration': '0', 'default': False, - 'replicaN': 1, 'name': 'default'}, - {'duration': '24h0m0s', 'default': True, - 'replicaN': 4, 'name': 'somename'} - ], + self.assertEqual([ + {'columns': ['name', 'duration', 'replicaN', 'default'], + 'values': [['default', '0', 1, False], + ['somename', '24h0m0s', 4, True]]}], rsp ) @@ -548,7 +563,7 @@ def test_create_retention_policy(self): ) def test_issue_143(self): - pt = partial(point, 'serie', timestamp='2015-03-30T16:16:37Z') + pt = partial(point, 'a_serie_name', timestamp='2015-03-30T16:16:37Z') pts = [ pt(value=15), pt(tags={'tag_1': 'value1'}, value=5), @@ -556,15 +571,19 @@ def test_issue_143(self): ] self.cli.write_points(pts) time.sleep(1) - rsp = self.cli.query('SELECT * FROM serie GROUP BY tag_1') + rsp = list(self.cli.query('SELECT * FROM a_serie_name GROUP BY tag_1')) # print(rsp, file=sys.stderr) - self.assertEqual({ - ('serie', (('tag_1', ''),)): [ - {'time': '2015-03-30T16:16:37Z', 'value': 15}], - ('serie', (('tag_1', 'value1'),)): [ - {'time': '2015-03-30T16:16:37Z', 'value': 5}], - ('serie', (('tag_1', 'value2'),)): [ - {'time': '2015-03-30T16:16:37Z', 'value': 10}]}, + + self.assertEqual([ + {'name': 'a_serie_name', 'columns': ['time', 'value'], + 'values': [['2015-03-30T16:16:37Z', 15]], + 'tags': {'tag_1': ''}}, + {'name': 'a_serie_name', 'columns': ['time', 'value'], + 'values': [['2015-03-30T16:16:37Z', 5]], + 'tags': {'tag_1': 'value1'}}, + {'name': 'a_serie_name', 'columns': ['time', 'value'], + 'values': [['2015-03-30T16:16:37Z', 10]], + 'tags': {'tag_1': 'value2'}}], rsp ) @@ -579,20 +598,38 @@ def test_issue_143(self): time.sleep(1) rsp = self.cli.query('SELECT * FROM serie2 GROUP BY tag1,tag2') # print(rsp, file=sys.stderr) + self.assertEqual([ + {'name': 'serie2', 'columns': ['time', 'value'], + 'values': [['2015-03-30T16:16:37Z', 0]], + 'tags': {'tag2': 'v1', 'tag1': 'value1'}}, + {'name': 'serie2', 'columns': ['time', 'value'], + 'values': [['2015-03-30T16:16:37Z', 5]], + 'tags': {'tag2': 'v2', 'tag1': 'value1'}}, + {'name': 'serie2', 'columns': ['time', 'value'], + 'values': [['2015-03-30T16:16:37Z', 10]], + 'tags': {'tag2': 'v1', 'tag1': 'value2'}}], + list(rsp) + ) + + d = all_tag2_equal_v1 = list(rsp[None, {'tag2': 'v1'}]) self.assertEqual( - { - ('serie2', (('tag1', 'value1'), ('tag2', 'v1'))): [ - {'time': '2015-03-30T16:16:37Z', 'value': 0} - ], - ('serie2', (('tag1', 'value1'), ('tag2', 'v2'))): [ - {'time': '2015-03-30T16:16:37Z', 'value': 5} - ], - ('serie2', (('tag1', 'value2'), ('tag2', 'v1'))): [ - {'time': '2015-03-30T16:16:37Z', 'value': 10}] - }, - rsp + [ + 2, + ['time', 'value'], + {'tag2': 'v1', 'tag1': 'value1'}, + ['2015-03-30T16:16:37Z', 0], + {'tag2': 'v1', 'tag1': 'value2'}, + ['2015-03-30T16:16:37Z', 10] + ], + [ + len(d), + d[0].columns, + d[0].tags, [d[0].values.time, d[0].values.value], + d[1].tags, [d[1].values.time, d[1].values.value] + ] ) + def test_tags_json_order(self): n_pts = 100 n_tags = 5 # that will make 120 possible orders (fact(5) == 120) @@ -647,6 +684,33 @@ def test_tags_json_order(self): rsp_tags = tuple(t[0] for t in serie_key[1]) self.assertEqual(expected_ordered_tags, rsp_tags) + + def test_query_multiple_series(self): + pt = partial(point, 'serie1', timestamp='2015-03-30T16:16:37Z') + pts = [ + pt(tags={'tag1': 'value1', 'tag2': 'v1'}, value=0), + #pt(tags={'tag1': 'value1', 'tag2': 'v2'}, value=5), + #pt(tags={'tag1': 'value2', 'tag2': 'v1'}, value=10), + ] + self.cli.write_points(pts) + + pt = partial(point, 'serie2', timestamp='1970-03-30T16:16:37Z') + pts = [ + pt(tags={'tag1': 'value1', 'tag2': 'v1'}, value=0, data1=33, data2="bla"), + #pt(tags={'tag1': 'value1', 'tag2': 'v2'}, value=5), + #pt(tags={'tag1': 'value2', 'tag2': 'v3'}, value=10), # data2="what"), + ] + self.cli.write_points(pts) + + rsp = self.cli.query('SELECT * FROM serie1, serie2') + print(rsp) + + # same but with the tags given : + #rsp = self.cli.query('SELECT * FROM serie1, serie2 GROUP BY *') + print(rsp) + + + ############################################################################ From 1c6df3f9280a7f7fe1a87baf819e51b9f82eea67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Fri, 3 Apr 2015 09:26:28 -0400 Subject: [PATCH 10/26] keep None as serie name in case of "system" query.. --- influxdb/resultset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/influxdb/resultset.py b/influxdb/resultset.py index 1e4dc650..2840614d 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -98,7 +98,7 @@ def __getitem__(self, key): # 'list retention' is in this case.. if key is None: for point in serie['values']: - yield Point('$SYSTEM$', serie['columns'], point) + yield Point(None, serie['columns'], point) elif name in (None, serie_name): # by default if no tags was provided then From 9d02c1c59c338ccfad52f5f57f68058903cfda64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Fri, 3 Apr 2015 09:27:48 -0400 Subject: [PATCH 11/26] docstrings + some better comments + 1 type check --- influxdb/resultset.py | 57 +++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/influxdb/resultset.py b/influxdb/resultset.py index 2840614d..394de607 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -7,7 +7,9 @@ _sentinel = object() +# could it be a namedtuple .. ?? class NamedValues(object): + def __init__(self, point): self._point = point @@ -27,6 +29,15 @@ def __repr__(self): class Point(object): def __init__(self, serie, columns, values, tags=None): + ''' + + :param serie: The name of the serie in which this point resides. + If None then it's a "system" point/result. + :param columns: The ordered list of the columns. + :param values: The actualy list of values of this point. Same order than columns. + :param tags: The eventual tags (dict) associated with the point. + :return: + ''' assert len(columns) == len(values) self.columns = columns self._point_values = values @@ -38,11 +49,11 @@ def __init__(self, serie, columns, values, tags=None): def __getitem__(self, tag_name): """Indexing a Point return the tag value associated with - the given tag name""" + the given tag name, if it exists""" return self._tags[tag_name] def __iter__(self): - """Iterating over a Point will return its tags names one per one""" + """Iterating over a Point will return its eventual tag names one per one""" return iter(self._tags) def __len__(self): @@ -50,7 +61,7 @@ def __len__(self): return len(self.columns) def __repr__(self): - return 'Point(values=%s, tags=%s)' % ( + return 'Point(values=(%s), tags=%s)' % ( ', '.join('%s=%r' % ( k, getattr(self.values, k)) for k in self.columns), self.tags) @@ -61,23 +72,18 @@ class ResultSet(object): """A wrapper around series results """ def __init__(self, series): - self.raw = series # ['results'] - results = series['results'] - if False: - self.have_tags = ( - results - and 'series' in results[0] - and results[0]['series'] - and 'tags' in results[0]['series'][0] - ) - # self.raw.update(series) # use the free update to set keys + self.raw = series def __getitem__(self, key): ''' :param key: Either a serie name or a 2-tuple(serie_name, tags_dict) - If the given serie name is None then any serie (matching the eventual - given tags) will be given its points one after the other. - :return: A generator yielding `Point`s matching the given key + If the given serie name is None then any serie (matching + the eventual given tags) will be given its points one + after the other. + :return: A generator yielding `Point`s matching the given key. + NB: + The order in which the points are yielded is actually undefined but + it might change.. ''' if isinstance(key, tuple): if 2 != len(key): @@ -85,17 +91,20 @@ def __getitem__(self, key): name = key[0] tags = key[1] if not isinstance(tags, dict): - raise TypeError('should be a dict') + raise TypeError('tags should be a dict') else: name = key tags = None + if not isinstance(name, (str, type(None))): + raise TypeError('serie_name must be an str or None') for result in self.raw['results']: for serie in result['series']: serie_name = serie.get('name', None) if serie_name is None: - # this is a "system" query or a query which doesn't returned a named "serie" - # 'list retention' is in this case.. + # this is a "system" query or a query which + # doesn't return a name attribute. + # like 'show retention policies' .. if key is None: for point in serie['values']: yield Point(None, serie['columns'], point) @@ -107,7 +116,13 @@ def __getitem__(self, key): serie_tags = serie.get('tags', {}) if tags: serie_matches = False + # if there are some tags requested, + # let's check them: for tag_name, tag_value in tags.items(): + # using _sentinel as I'm not sure that "None" + # could be used, because it could be a valid + # serie_tags value : when a serie has no such tag + # then I think it's set to /null/None/.. TBC.. serie_tag_value = serie_tags.get(tag_name, _sentinel) if serie_tag_value != tag_value: break @@ -125,8 +140,8 @@ def __iter__(self): ''' Iterating a ResultSet will yield one dict instance per serie result. ''' for results in self.raw['results']: - for many_series in results['series']: - yield many_series + for serie in results['series']: + yield serie #def __len__(self): # return len(self.raw) From a93b2178fe55de5c124477b90f3f7d625c1e51b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Fri, 3 Apr 2015 09:28:00 -0400 Subject: [PATCH 12/26] to be decided .. --- influxdb/client.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index 989405f7..dc3ae377 100755 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -301,8 +301,11 @@ def get_list_database(self): """ Get the list of databases """ + rsp = self.query("SHOW DATABASES") #['results'][0]['points'] + # TODO: to be decided the format of what we return ;) lrsp = list(rsp) + return [value[0] for value in lrsp[0].get('values', [])] #return rsp['results'][0]['points'] @@ -344,9 +347,13 @@ def get_list_retention_policies(self, database=None): """ Get the list of retention policies """ - return self.query( + rsp = self.query( "SHOW RETENTION POLICIES %s" % (database or self._database) - )['results'][0]['points'] + ) + # TODO: same here + lrsp = list(rsp) + points = list(rsp[None]) + return lrsp def get_list_series(self, database=None): """ From 863a5d4e804783d327f411dd7af83eb0d79a3508 Mon Sep 17 00:00:00 2001 From: aviau Date: Fri, 3 Apr 2015 10:46:03 -0400 Subject: [PATCH 13/26] Fixed TestResultSet + Added TestPoint --- influxdb/point.py | 71 +++++++++++++++++++++++++++++++ influxdb/resultset.py | 73 +++----------------------------- tests/influxdb/point_test.py | 38 +++++++++++++++++ tests/influxdb/resultset_test.py | 66 +++++++++++------------------ 4 files changed, 138 insertions(+), 110 deletions(-) create mode 100644 influxdb/point.py create mode 100644 tests/influxdb/point_test.py diff --git a/influxdb/point.py b/influxdb/point.py new file mode 100644 index 00000000..51127d81 --- /dev/null +++ b/influxdb/point.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + + +# could it be a namedtuple .. ?? +class NamedValues(object): + + def __init__(self, point): + self._point = point + + def __getattr__(self, item): + try: + index = self._point.columns.index(item) + except ValueError: + raise AttributeError('Point have no such attribute (%r)' % item) + return self._point._point_values[index] + + def __repr__(self): + return 'Values(%s)' % ', '.join( + '%s=%r' % (k, self._point._point_values[k]) + for k in self._point.columns) + + +class Point(object): + + def __init__(self, serie, columns, values, tags=None): + """ + + :param serie: The name of the serie in which this point resides. + If None then it's a "system" point/result. + :param columns: The ordered list of the columns. + :param values: The actualy list of values of this point. Same order than columns. + :param tags: The eventual tags (dict) associated with the point. + :return: + """ + assert len(columns) == len(values) + self.columns = columns + self._point_values = values + if tags is None: + tags = {} + self.serie = serie + self.tags = tags + self.values = NamedValues(self) + + def __getitem__(self, tag_name): + """Indexing a Point return the tag value associated with + the given tag name, if it exists""" + return self._tags[tag_name] + + def __iter__(self): + """Iterating over a Point will return its eventual tag names one per one""" + return iter(self._tags) + + def __len__(self): + """The len of a Point is its number of columns/values""" + return len(self.columns) + + def __repr__(self): + return 'Point(values=(%s), tags=%s)' % ( + ', '.join('%s=%r' % ( + k, getattr(self.values, k)) for k in self.columns), + self.tags) + + def __eq__(self, other): + return (isinstance(other, self.__class__) + and self.tags == other.tags + and self._point_values == other._point_values + and self.serie == other.serie + and self.columns == other.columns) + + def __ne__(self, other): + return not self.__eq__(other) diff --git a/influxdb/resultset.py b/influxdb/resultset.py index 394de607..ce9f665b 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -1,73 +1,10 @@ # -*- coding: utf-8 -*- -import collections -from collections import namedtuple - +from influxdb.point import Point _sentinel = object() -# could it be a namedtuple .. ?? -class NamedValues(object): - - def __init__(self, point): - self._point = point - - def __getattr__(self, item): - try: - index = self._point.columns.index(item) - except ValueError: - raise AttributeError('Point have no such attribute (%r)' % item) - return self._point._point_values[index] - - def __repr__(self): - return 'Values(%s)' % ', '.join( - '%s=%r' % (k, self._point._point_values[k]) - for k in self._point.columns) - - -class Point(object): - - def __init__(self, serie, columns, values, tags=None): - ''' - - :param serie: The name of the serie in which this point resides. - If None then it's a "system" point/result. - :param columns: The ordered list of the columns. - :param values: The actualy list of values of this point. Same order than columns. - :param tags: The eventual tags (dict) associated with the point. - :return: - ''' - assert len(columns) == len(values) - self.columns = columns - self._point_values = values - if tags is None: - tags = {} - self.serie = serie - self.tags = tags - self.values = NamedValues(self) - - def __getitem__(self, tag_name): - """Indexing a Point return the tag value associated with - the given tag name, if it exists""" - return self._tags[tag_name] - - def __iter__(self): - """Iterating over a Point will return its eventual tag names one per one""" - return iter(self._tags) - - def __len__(self): - """The len of a Point is its number of columns/values""" - return len(self.columns) - - def __repr__(self): - return 'Point(values=(%s), tags=%s)' % ( - ', '.join('%s=%r' % ( - k, getattr(self.values, k)) for k in self.columns), - self.tags) - - - class ResultSet(object): """A wrapper around series results """ @@ -75,7 +12,7 @@ def __init__(self, series): self.raw = series def __getitem__(self, key): - ''' + """ :param key: Either a serie name or a 2-tuple(serie_name, tags_dict) If the given serie name is None then any serie (matching the eventual given tags) will be given its points one @@ -84,7 +21,7 @@ def __getitem__(self, key): NB: The order in which the points are yielded is actually undefined but it might change.. - ''' + """ if isinstance(key, tuple): if 2 != len(key): raise TypeError('only 2-tuples allowed') @@ -137,8 +74,8 @@ def __repr__(self): return str(self.raw) def __iter__(self): - ''' Iterating a ResultSet will yield one dict instance per serie result. - ''' + """ Iterating a ResultSet will yield one dict instance per serie result. + """ for results in self.raw['results']: for serie in results['series']: yield serie diff --git a/tests/influxdb/point_test.py b/tests/influxdb/point_test.py new file mode 100644 index 00000000..b50e2174 --- /dev/null +++ b/tests/influxdb/point_test.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +import unittest + +from influxdb.point import Point + + +class TestPoint(unittest.TestCase): + + def test_point(self): + point = Point( + "serie_name", + ['col1', 'col2'], + [1, '2'], + tags={ + "SWAG": True, + "ALLO": "BYE" + } + ) + + self.assertEqual(point.columns, ['col1', 'col2']) + self.assertEqual(point.tags, {"SWAG": True, "ALLO": "BYE"}) + self.assertEqual(point.values.col1, 1) + self.assertEqual(point.values.col2, '2') + self.assertEqual( + str(point), + "Point(values=(col1=1, col2='2')," + " tags={'ALLO': 'BYE', 'SWAG': True})" + ) + + def test_point_eq(self): + point1 = Point("serie_name", ['col1', 'col2'], [1, '2'], + tags={"SWAG": True, "ALLO": "BYE"}) + + point2 = Point("serie_name", ['col1', 'col2'], [1, '2'], + tags={"SWAG": True, "ALLO": "BYE"}) + + self.assertEqual(point1, point2) diff --git a/tests/influxdb/resultset_test.py b/tests/influxdb/resultset_test.py index 8f1768e8..f77a5a22 100644 --- a/tests/influxdb/resultset_test.py +++ b/tests/influxdb/resultset_test.py @@ -3,6 +3,7 @@ import unittest from influxdb.resultset import ResultSet +from influxdb.point import Point class TestResultSet(unittest.TestCase): @@ -36,57 +37,38 @@ def setUp(self): self.rs = ResultSet(self.query_response) def test_filter_by_name(self): - self.assertItemsEqual( - self.rs['cpu_load_short'], + self.assertEqual( + list(self.rs['cpu_load_short']), [ - { - "tags": {"host": "server01", "region": "us-west"}, - "points": [ - {"time": "2015-01-29T21:51:28.968422294Z", - "value": 0.64} - ] - }, - { - "tags": {"host": "server02", "region": "us-west"}, - "points": [ - {"time": "2015-01-29T21:51:28.968422294Z", - "value": 0.64} - ] - } + Point("cpu_load_short", ["time", "value"], + ["2015-01-29T21:51:28.968422294Z", 0.64], + tags={"host": "server01", "region": "us-west"}), + Point("cpu_load_short", ["time", "value"], + ["2015-01-29T21:51:28.968422294Z", 0.64], + tags={"host": "server02", "region": "us-west"}) ] ) def test_filter_by_tags(self): - self.assertItemsEqual( - self.rs[('cpu_load_short', {"host": "server01"})], + self.assertEqual( + list(self.rs[('cpu_load_short', {"host": "server01"})]), [ - { - "tags": {"host": "server01", "region": "us-west"}, - "points": [ - {"time": "2015-01-29T21:51:28.968422294Z", - "value": 0.64} - ] - } + Point( + "cpu_load_short", ["time", "value"], + ["2015-01-29T21:51:28.968422294Z", 0.64], + tags={"host": "server01", "region": "us-west"} + ) ] ) - self.assertItemsEqual( - self.rs[('cpu_load_short', {"region": "us-west"})], + self.assertEqual( + list(self.rs[('cpu_load_short', {"region": "us-west"})]), [ - { - "tags": {"host": "server01", "region": "us-west"}, - "points": [ - {"time": "2015-01-29T21:51:28.968422294Z", - "value": 0.64} - ] - }, - { - "tags": {"host": "server02", "region": "us-west"}, - "points": [ - {"time": "2015-01-29T21:51:28.968422294Z", - "value": 0.64} - ] - } + Point("cpu_load_short", ["time", "value"], + ["2015-01-29T21:51:28.968422294Z", 0.64], + tags={"host": "server01", "region": "us-west"}), + Point("cpu_load_short", ["time", "value"], + ["2015-01-29T21:51:28.968422294Z", 0.64], + tags={"host": "server02", "region": "us-west"}), ] ) - From 922a0a58c550c2672834e373899db57f93983dee Mon Sep 17 00:00:00 2001 From: aviau Date: Fri, 3 Apr 2015 11:08:33 -0400 Subject: [PATCH 14/26] ResultSet: added Keys() and items() functions --- influxdb/resultset.py | 90 +++++++++++++++++++------------- tests/influxdb/resultset_test.py | 44 ++++++++++++++++ 2 files changed, 99 insertions(+), 35 deletions(-) diff --git a/influxdb/resultset.py b/influxdb/resultset.py index ce9f665b..e3e3d37c 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -35,40 +35,23 @@ def __getitem__(self, key): if not isinstance(name, (str, type(None))): raise TypeError('serie_name must be an str or None') - for result in self.raw['results']: - for serie in result['series']: - serie_name = serie.get('name', None) - if serie_name is None: - # this is a "system" query or a query which - # doesn't return a name attribute. - # like 'show retention policies' .. - if key is None: - for point in serie['values']: - yield Point(None, serie['columns'], point) + for serie in self._get_series(): + serie_name = serie.get('name', None) + if serie_name is None: + # this is a "system" query or a query which + # doesn't return a name attribute. + # like 'show retention policies' .. + if key is None: + for point in serie['values']: + yield Point(None, serie['columns'], point) - elif name in (None, serie_name): - # by default if no tags was provided then - # we will matches every returned serie - serie_matches = True - serie_tags = serie.get('tags', {}) - if tags: - serie_matches = False - # if there are some tags requested, - # let's check them: - for tag_name, tag_value in tags.items(): - # using _sentinel as I'm not sure that "None" - # could be used, because it could be a valid - # serie_tags value : when a serie has no such tag - # then I think it's set to /null/None/.. TBC.. - serie_tag_value = serie_tags.get(tag_name, _sentinel) - if serie_tag_value != tag_value: - break - else: - serie_matches = True - - if serie_matches: - for point in serie['values']: - yield Point(serie_name, serie['columns'], point, serie_tags) + elif name in (None, serie_name): + # by default if no tags was provided then + # we will matches every returned serie + serie_tags = serie.get('tags', {}) + if tags is None or self._tag_matches(serie_tags, tags): + for point in serie['values']: + yield Point(serie_name, serie['columns'], point, serie_tags) def __repr__(self): return str(self.raw) @@ -80,5 +63,42 @@ def __iter__(self): for serie in results['series']: yield serie - #def __len__(self): - # return len(self.raw) + def _tag_matches(self, tags, filter): + """Checks if all key/values in filter match in tags""" + for tag_name, tag_value in filter.items(): + # using _sentinel as I'm not sure that "None" + # could be used, because it could be a valid + # serie_tags value : when a serie has no such tag + # then I think it's set to /null/None/.. TBC.. + serie_tag_value = tags.get(tag_name, _sentinel) + if serie_tag_value != tag_value: + return False + return True + + def _get_series(self): + """Returns all series""" + series = [] + try: + for result in self.raw['results']: + series.extend(result['series']) + except KeyError: + pass + return series + + def __len__(self): + return len(self.keys()) + + def keys(self): + keys = [] + for serie in self._get_series(): + keys.append((serie['name'], serie['tags'])) + return keys + + def items(self): + items = [] + for serie in self._get_series(): + serie_key = (serie['name'], serie['tags']) + items.append( + (serie_key, self.__getitem__(serie_key)) + ) + return items \ No newline at end of file diff --git a/tests/influxdb/resultset_test.py b/tests/influxdb/resultset_test.py index f77a5a22..0a727e8d 100644 --- a/tests/influxdb/resultset_test.py +++ b/tests/influxdb/resultset_test.py @@ -72,3 +72,47 @@ def test_filter_by_tags(self): tags={"host": "server02", "region": "us-west"}), ] ) + + def test_keys(self): + self.assertItemsEqual( + self.rs.keys(), + [ + ('cpu_load_short', {'host': 'server01', 'region': 'us-west'}), + ('cpu_load_short', {'host': 'server02', 'region': 'us-west'}), + ('other_serie', {'host': 'server01', 'region': 'us-west'}) + ] + ) + + def test_len(self): + self.assertEqual( + len(self.rs), + 3 + ) + + def test_items(self): + items = list(self.rs.items()) + items_lists = [(item[0], list(item[1])) for item in items] + + self.assertEqual( + items_lists, + [ + ( + ('cpu_load_short', {'host': 'server01', 'region': 'us-west'}), + [Point("cpu_load_short", ["time", "value"], + ["2015-01-29T21:51:28.968422294Z", 0.64], + tags={"host": "server01", "region": "us-west"})] + ), + ( + ('cpu_load_short', {'host': 'server02', 'region': 'us-west'}), + [Point("cpu_load_short", ["time", "value"], + ["2015-01-29T21:51:28.968422294Z", 0.64], + tags={"host": "server02", "region": "us-west"})] + ), + ( + ('other_serie', {'host': 'server01', 'region': 'us-west'}), + [Point("other_serie", ["time", "value"], + ["2015-01-29T21:51:28.968422294Z", 0.64], + tags={"host": "server01", "region": "us-west"})] + ) + ] + ) From 75b6e480a38c4e5d8ed20eb5ee02a532b905a412 Mon Sep 17 00:00:00 2001 From: aviau Date: Fri, 3 Apr 2015 11:19:25 -0400 Subject: [PATCH 15/26] ResultSet: added as_dict() --- influxdb/point.py | 6 ++++++ tests/influxdb/point_test.py | 22 ++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/influxdb/point.py b/influxdb/point.py index 51127d81..90aac7d4 100644 --- a/influxdb/point.py +++ b/influxdb/point.py @@ -69,3 +69,9 @@ def __eq__(self, other): def __ne__(self, other): return not self.__eq__(other) + + def as_dict(self): + return { + "serie": self.serie, + "point": [{col: getattr(self.values, col)} for col in self.columns] + } diff --git a/tests/influxdb/point_test.py b/tests/influxdb/point_test.py index b50e2174..96ffb9da 100644 --- a/tests/influxdb/point_test.py +++ b/tests/influxdb/point_test.py @@ -6,9 +6,8 @@ class TestPoint(unittest.TestCase): - - def test_point(self): - point = Point( + def setUp(self): + self.point = Point( "serie_name", ['col1', 'col2'], [1, '2'], @@ -18,12 +17,13 @@ def test_point(self): } ) - self.assertEqual(point.columns, ['col1', 'col2']) - self.assertEqual(point.tags, {"SWAG": True, "ALLO": "BYE"}) - self.assertEqual(point.values.col1, 1) - self.assertEqual(point.values.col2, '2') + def test_point(self): + self.assertEqual(self.point.columns, ['col1', 'col2']) + self.assertEqual(self.point.tags, {"SWAG": True, "ALLO": "BYE"}) + self.assertEqual(self.point.values.col1, 1) + self.assertEqual(self.point.values.col2, '2') self.assertEqual( - str(point), + str(self.point), "Point(values=(col1=1, col2='2')," " tags={'ALLO': 'BYE', 'SWAG': True})" ) @@ -36,3 +36,9 @@ def test_point_eq(self): tags={"SWAG": True, "ALLO": "BYE"}) self.assertEqual(point1, point2) + + def test_as_dict(self): + self.assertEqual( + self.point.as_dict(), + {'point': [{'col1': 1}, {'col2': '2'}], 'serie': 'serie_name'} + ) From 2bf9e421c7909b4f4a02e1656829a47425c5a826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Tue, 7 Apr 2015 10:01:58 -0400 Subject: [PATCH 16/26] Fix: serie can have no associated tags --- influxdb/resultset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/influxdb/resultset.py b/influxdb/resultset.py index e3e3d37c..8011f80a 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -91,7 +91,7 @@ def __len__(self): def keys(self): keys = [] for serie in self._get_series(): - keys.append((serie['name'], serie['tags'])) + keys.append((serie['name'], serie.get('tags', None))) return keys def items(self): From c2f22844e4666780a202493e62d05263b08593d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Tue, 7 Apr 2015 10:15:42 -0400 Subject: [PATCH 17/26] double Fix: serie can miss its 'name' & 'tags' attribute + if a serie is the result of a "system" query: it has no name attribute. + a serie can also have no associated tags. --- influxdb/resultset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/influxdb/resultset.py b/influxdb/resultset.py index 8011f80a..0a6c99b4 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -91,14 +91,14 @@ def __len__(self): def keys(self): keys = [] for serie in self._get_series(): - keys.append((serie['name'], serie.get('tags', None))) + keys.append((serie.get('name', None), serie.get('tags', None))) return keys def items(self): items = [] for serie in self._get_series(): - serie_key = (serie['name'], serie['tags']) + serie_key = (serie.get('name', None), serie.get('tags', None)) items.append( - (serie_key, self.__getitem__(serie_key)) + (serie_key, self[serie_key]) ) return items \ No newline at end of file From 670fdea1953aa6c7c77c865babf5dd4f4f90f530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Tue, 7 Apr 2015 10:36:05 -0400 Subject: [PATCH 18/26] client_test: Fixed some things: 1) cli.query() which now returns a ResultSet, which must be list()ed in order to obtain the list of series. 2) assertListEqual instead of assertDictEqual for a list result. TODO: but now we have to fix the results format. --- tests/influxdb/client_test.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/influxdb/client_test.py b/tests/influxdb/client_test.py index 5dc03e3d..14c3aa8d 100644 --- a/tests/influxdb/client_test.py +++ b/tests/influxdb/client_test.py @@ -265,12 +265,13 @@ def test_query(self): "http://localhost:8086/query", text=example_response ) - self.assertDictEqual( - self.cli.query('select * from foo'), - {'cpu_load_short': + rs = self.cli.query('select * from foo') + self.assertListEqual( + list(rs), + [{'cpu_load_short': [{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}], 'sdfsdfsdf': - [{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}]} + [{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}]}] ) @unittest.skip('Not implemented for 0.9') From 48be83720ade3efc6d54a0ab66ba3946a522baf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Tue, 7 Apr 2015 10:36:51 -0400 Subject: [PATCH 19/26] clean --- tests/influxdb/client_test_with_server.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/influxdb/client_test_with_server.py b/tests/influxdb/client_test_with_server.py index a54a3125..bad3c0d2 100644 --- a/tests/influxdb/client_test_with_server.py +++ b/tests/influxdb/client_test_with_server.py @@ -287,9 +287,8 @@ def test_fresh_server_no_db(self): def test_create_database(self): self.assertIsNone(self.cli.create_database('new_db_1')) self.assertIsNone(self.cli.create_database('new_db_2')) - rsp = self.cli.get_list_database() self.assertEqual( - rsp, + self.cli.get_list_database(), ['new_db_1', 'new_db_2'] ) From 8ba6ad1705f4a7c4651e3cdccc2afa0931ac4370 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Tue, 7 Apr 2015 10:39:22 -0400 Subject: [PATCH 20/26] workaround see: https://groups.google.com/forum/?hl=fr#!topic/influxdb/ZgwXfh1sqRU https://github.com/influxdb/influxdb/issues/2169 --- tests/influxdb/client_test_with_server.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/influxdb/client_test_with_server.py b/tests/influxdb/client_test_with_server.py index bad3c0d2..a0d2cb75 100644 --- a/tests/influxdb/client_test_with_server.py +++ b/tests/influxdb/client_test_with_server.py @@ -701,7 +701,7 @@ def test_query_multiple_series(self): ] self.cli.write_points(pts) - rsp = self.cli.query('SELECT * FROM serie1, serie2') + rsp = self.cli.query('SELECT * FROM serie1; SELECT * FROM serie2') print(rsp) # same but with the tags given : From f487e0ea6bfff68d6ede64e2f58caed58296f285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Tue, 7 Apr 2015 10:40:48 -0400 Subject: [PATCH 21/26] Proposed Fix: expand the ResultSet response by using list() on it. Other solution is to return the ResultSet instance but then we must adapt all call sites.. To be decided.. --- influxdb/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index dc3ae377..db5bbdfc 100755 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -362,13 +362,13 @@ def get_list_series(self, database=None): rsp = self.query("SHOW SERIES", database=database) print "RSP", rsp.raw print "RSP", rsp['results'] - return rsp + return list(rsp) def get_list_users(self): """ Get the list of users """ - return self.query("SHOW USERS") + return list(self.query("SHOW USERS")) def delete_series(self, name, database=None): database = database or self._database From c226c13e091034b4767b8fbf60b83b2b00de53a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20Starck?= Date: Wed, 8 Apr 2015 10:30:53 -0400 Subject: [PATCH 22/26] Changed/decided of format for response of "system" queries show series show list retention --- tests/influxdb/client_test_with_server.py | 35 ++++++++++++----------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/tests/influxdb/client_test_with_server.py b/tests/influxdb/client_test_with_server.py index a0d2cb75..0961455f 100644 --- a/tests/influxdb/client_test_with_server.py +++ b/tests/influxdb/client_test_with_server.py @@ -521,30 +521,34 @@ def test_get_list_series_empty(self): def test_get_list_series_non_empty(self): self.cli.write_points(dummy_point) rsp = self.cli.get_list_series() - self.assertEqual( - {'cpu_load_short': [ - {'region': 'us-west', 'host': 'server01', '_id': 1}]}, - rsp - ) + self.assertEqual([ + { + 'serie_name': 'cpu_load_short', + '_id': 1, + "tags": { + "host": "server01", + "region": "us-west" + } + }], + rsp) def test_default_retention_policy(self): rsp = self.cli.get_list_retention_policies() self.assertEqual( [ - {'duration': '0', 'default': True, - 'replicaN': 1, 'name': 'default'}], + {'name': 'default', 'duration': '0', 'replicaN': 1, 'default': True} + ], rsp ) def test_create_retention_policy_default(self): - rsp = self.cli.create_retention_policy('somename', '1d', 4, - default=True) - self.assertIsNone(rsp) + self.cli.create_retention_policy('somename', '1d', 4, default=True) + self.cli.create_retention_policy('another', '2d', 3, default=False) rsp = self.cli.get_list_retention_policies() self.assertEqual([ - {'columns': ['name', 'duration', 'replicaN', 'default'], - 'values': [['default', '0', 1, False], - ['somename', '24h0m0s', 4, True]]}], + {'name': 'somename', 'duration': '1d', 'replicaN': 4, 'default': True}, + {'name': 'another', 'duration': '2d', 'replicaN': 3, 'default': False} + ], rsp ) @@ -553,10 +557,7 @@ def test_create_retention_policy(self): rsp = self.cli.get_list_retention_policies() self.assertEqual( [ - {'duration': '0', 'default': True, 'replicaN': 1, - 'name': 'default'}, - {'duration': '24h0m0s', 'default': False, 'replicaN': 4, - 'name': 'somename'} + {'name': 'somename', 'duration': '1d', 'replicaN': 4, 'default': False}, ], rsp ) From 141e664b0f9f6f181a6d8dbff1bced85bab85405 Mon Sep 17 00:00:00 2001 From: aviau Date: Wed, 8 Apr 2015 10:34:51 -0400 Subject: [PATCH 23/26] Removed Points object --- influxdb/point.py | 77 -------------------------------- influxdb/resultset.py | 15 ++++--- tests/influxdb/point_test.py | 44 ------------------ tests/influxdb/resultset_test.py | 47 ++++++++----------- 4 files changed, 28 insertions(+), 155 deletions(-) delete mode 100644 influxdb/point.py delete mode 100644 tests/influxdb/point_test.py diff --git a/influxdb/point.py b/influxdb/point.py deleted file mode 100644 index 90aac7d4..00000000 --- a/influxdb/point.py +++ /dev/null @@ -1,77 +0,0 @@ -# -*- coding: utf-8 -*- - - -# could it be a namedtuple .. ?? -class NamedValues(object): - - def __init__(self, point): - self._point = point - - def __getattr__(self, item): - try: - index = self._point.columns.index(item) - except ValueError: - raise AttributeError('Point have no such attribute (%r)' % item) - return self._point._point_values[index] - - def __repr__(self): - return 'Values(%s)' % ', '.join( - '%s=%r' % (k, self._point._point_values[k]) - for k in self._point.columns) - - -class Point(object): - - def __init__(self, serie, columns, values, tags=None): - """ - - :param serie: The name of the serie in which this point resides. - If None then it's a "system" point/result. - :param columns: The ordered list of the columns. - :param values: The actualy list of values of this point. Same order than columns. - :param tags: The eventual tags (dict) associated with the point. - :return: - """ - assert len(columns) == len(values) - self.columns = columns - self._point_values = values - if tags is None: - tags = {} - self.serie = serie - self.tags = tags - self.values = NamedValues(self) - - def __getitem__(self, tag_name): - """Indexing a Point return the tag value associated with - the given tag name, if it exists""" - return self._tags[tag_name] - - def __iter__(self): - """Iterating over a Point will return its eventual tag names one per one""" - return iter(self._tags) - - def __len__(self): - """The len of a Point is its number of columns/values""" - return len(self.columns) - - def __repr__(self): - return 'Point(values=(%s), tags=%s)' % ( - ', '.join('%s=%r' % ( - k, getattr(self.values, k)) for k in self.columns), - self.tags) - - def __eq__(self, other): - return (isinstance(other, self.__class__) - and self.tags == other.tags - and self._point_values == other._point_values - and self.serie == other.serie - and self.columns == other.columns) - - def __ne__(self, other): - return not self.__eq__(other) - - def as_dict(self): - return { - "serie": self.serie, - "point": [{col: getattr(self.values, col)} for col in self.columns] - } diff --git a/influxdb/resultset.py b/influxdb/resultset.py index 0a6c99b4..0d8ce1c3 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- -from influxdb.point import Point - _sentinel = object() @@ -43,7 +41,7 @@ def __getitem__(self, key): # like 'show retention policies' .. if key is None: for point in serie['values']: - yield Point(None, serie['columns'], point) + yield self.point_from_cols_vals(serie['columns'], point) elif name in (None, serie_name): # by default if no tags was provided then @@ -51,7 +49,7 @@ def __getitem__(self, key): serie_tags = serie.get('tags', {}) if tags is None or self._tag_matches(serie_tags, tags): for point in serie['values']: - yield Point(serie_name, serie['columns'], point, serie_tags) + yield self.point_from_cols_vals(serie['columns'], point) def __repr__(self): return str(self.raw) @@ -101,4 +99,11 @@ def items(self): items.append( (serie_key, self[serie_key]) ) - return items \ No newline at end of file + return items + + @staticmethod + def point_from_cols_vals(cols, vals): + point = {} + for col_index, col_name in enumerate(cols): + point[col_name] = vals[col_index] + return point diff --git a/tests/influxdb/point_test.py b/tests/influxdb/point_test.py deleted file mode 100644 index 96ffb9da..00000000 --- a/tests/influxdb/point_test.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- - -import unittest - -from influxdb.point import Point - - -class TestPoint(unittest.TestCase): - def setUp(self): - self.point = Point( - "serie_name", - ['col1', 'col2'], - [1, '2'], - tags={ - "SWAG": True, - "ALLO": "BYE" - } - ) - - def test_point(self): - self.assertEqual(self.point.columns, ['col1', 'col2']) - self.assertEqual(self.point.tags, {"SWAG": True, "ALLO": "BYE"}) - self.assertEqual(self.point.values.col1, 1) - self.assertEqual(self.point.values.col2, '2') - self.assertEqual( - str(self.point), - "Point(values=(col1=1, col2='2')," - " tags={'ALLO': 'BYE', 'SWAG': True})" - ) - - def test_point_eq(self): - point1 = Point("serie_name", ['col1', 'col2'], [1, '2'], - tags={"SWAG": True, "ALLO": "BYE"}) - - point2 = Point("serie_name", ['col1', 'col2'], [1, '2'], - tags={"SWAG": True, "ALLO": "BYE"}) - - self.assertEqual(point1, point2) - - def test_as_dict(self): - self.assertEqual( - self.point.as_dict(), - {'point': [{'col1': 1}, {'col2': '2'}], 'serie': 'serie_name'} - ) diff --git a/tests/influxdb/resultset_test.py b/tests/influxdb/resultset_test.py index 0a727e8d..00872f15 100644 --- a/tests/influxdb/resultset_test.py +++ b/tests/influxdb/resultset_test.py @@ -3,7 +3,6 @@ import unittest from influxdb.resultset import ResultSet -from influxdb.point import Point class TestResultSet(unittest.TestCase): @@ -40,36 +39,22 @@ def test_filter_by_name(self): self.assertEqual( list(self.rs['cpu_load_short']), [ - Point("cpu_load_short", ["time", "value"], - ["2015-01-29T21:51:28.968422294Z", 0.64], - tags={"host": "server01", "region": "us-west"}), - Point("cpu_load_short", ["time", "value"], - ["2015-01-29T21:51:28.968422294Z", 0.64], - tags={"host": "server02", "region": "us-west"}) + {'value': 0.64, 'time': '2015-01-29T21:51:28.968422294Z'}, + {'value': 0.64, 'time': '2015-01-29T21:51:28.968422294Z'} ] ) def test_filter_by_tags(self): self.assertEqual( list(self.rs[('cpu_load_short', {"host": "server01"})]), - [ - Point( - "cpu_load_short", ["time", "value"], - ["2015-01-29T21:51:28.968422294Z", 0.64], - tags={"host": "server01", "region": "us-west"} - ) - ] + [{'time': '2015-01-29T21:51:28.968422294Z', 'value': 0.64}] ) self.assertEqual( list(self.rs[('cpu_load_short', {"region": "us-west"})]), [ - Point("cpu_load_short", ["time", "value"], - ["2015-01-29T21:51:28.968422294Z", 0.64], - tags={"host": "server01", "region": "us-west"}), - Point("cpu_load_short", ["time", "value"], - ["2015-01-29T21:51:28.968422294Z", 0.64], - tags={"host": "server02", "region": "us-west"}), + {'value': 0.64, 'time': '2015-01-29T21:51:28.968422294Z'}, + {'value': 0.64, 'time': '2015-01-29T21:51:28.968422294Z'} ] ) @@ -98,21 +83,25 @@ def test_items(self): [ ( ('cpu_load_short', {'host': 'server01', 'region': 'us-west'}), - [Point("cpu_load_short", ["time", "value"], - ["2015-01-29T21:51:28.968422294Z", 0.64], - tags={"host": "server01", "region": "us-west"})] + [{'value': 0.64, 'time': '2015-01-29T21:51:28.968422294Z'}] ), ( ('cpu_load_short', {'host': 'server02', 'region': 'us-west'}), - [Point("cpu_load_short", ["time", "value"], - ["2015-01-29T21:51:28.968422294Z", 0.64], - tags={"host": "server02", "region": "us-west"})] + [{'value': 0.64, 'time': '2015-01-29T21:51:28.968422294Z'}] ), ( ('other_serie', {'host': 'server01', 'region': 'us-west'}), - [Point("other_serie", ["time", "value"], - ["2015-01-29T21:51:28.968422294Z", 0.64], - tags={"host": "server01", "region": "us-west"})] + [{'value': 0.64, 'time': '2015-01-29T21:51:28.968422294Z'}] ) ] ) + + def test_point_from_cols_vals(self): + cols = ['col1', 'col2'] + vals = [1, '2'] + + point = ResultSet.point_from_cols_vals(cols, vals) + self.assertDictEqual( + point, + {'col1': 1, 'col2': '2'} + ) From 728a4882b57e668f760ee86062cc7b88cb99cd38 Mon Sep 17 00:00:00 2001 From: aviau Date: Wed, 8 Apr 2015 14:52:13 -0400 Subject: [PATCH 24/26] Finalize ResultSet --- influxdb/client.py | 25 ++- influxdb/resultset.py | 29 +-- tests/influxdb/client_test.py | 26 +-- tests/influxdb/client_test_with_server.py | 226 +++++++--------------- tests/influxdb/resultset_test.py | 47 ++++- 5 files changed, 156 insertions(+), 197 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index db5bbdfc..4438b091 100755 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -301,13 +301,7 @@ def get_list_database(self): """ Get the list of databases """ - - rsp = self.query("SHOW DATABASES") #['results'][0]['points'] - # TODO: to be decided the format of what we return ;) - lrsp = list(rsp) - - return [value[0] for value in lrsp[0].get('values', [])] - #return rsp['results'][0]['points'] + return list(self.query("SHOW DATABASES")['results']) def create_database(self, dbname): """ @@ -350,19 +344,22 @@ def get_list_retention_policies(self, database=None): rsp = self.query( "SHOW RETENTION POLICIES %s" % (database or self._database) ) - # TODO: same here - lrsp = list(rsp) - points = list(rsp[None]) - return lrsp + return list(rsp['results']) def get_list_series(self, database=None): """ Get the list of series """ rsp = self.query("SHOW SERIES", database=database) - print "RSP", rsp.raw - print "RSP", rsp['results'] - return list(rsp) + series = [] + for serie in rsp.items(): + series.append( + { + "name": serie[0][0], + "tags": list(serie[1]) + } + ) + return series def get_list_users(self): """ diff --git a/influxdb/resultset.py b/influxdb/resultset.py index 0d8ce1c3..1a0cd0b5 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -25,31 +25,37 @@ def __getitem__(self, key): raise TypeError('only 2-tuples allowed') name = key[0] tags = key[1] - if not isinstance(tags, dict): + if not isinstance(tags, dict) and tags is not None: raise TypeError('tags should be a dict') else: name = key tags = None - if not isinstance(name, (str, type(None))): + if not isinstance(name, (str, type(None), type(u''))): raise TypeError('serie_name must be an str or None') for serie in self._get_series(): - serie_name = serie.get('name', None) + serie_name = serie.get('name', 'results') if serie_name is None: # this is a "system" query or a query which # doesn't return a name attribute. # like 'show retention policies' .. if key is None: for point in serie['values']: - yield self.point_from_cols_vals(serie['columns'], point) + yield self.point_from_cols_vals( + serie['columns'], + point + ) elif name in (None, serie_name): # by default if no tags was provided then # we will matches every returned serie serie_tags = serie.get('tags', {}) if tags is None or self._tag_matches(serie_tags, tags): - for point in serie['values']: - yield self.point_from_cols_vals(serie['columns'], point) + for point in serie.get('values', []): + yield self.point_from_cols_vals( + serie['columns'], + point + ) def __repr__(self): return str(self.raw) @@ -57,9 +63,8 @@ def __repr__(self): def __iter__(self): """ Iterating a ResultSet will yield one dict instance per serie result. """ - for results in self.raw['results']: - for serie in results['series']: - yield serie + for key in self.keys(): + yield list(self.__getitem__(key)) def _tag_matches(self, tags, filter): """Checks if all key/values in filter match in tags""" @@ -89,13 +94,15 @@ def __len__(self): def keys(self): keys = [] for serie in self._get_series(): - keys.append((serie.get('name', None), serie.get('tags', None))) + keys.append( + (serie.get('name', 'results'), serie.get('tags', None)) + ) return keys def items(self): items = [] for serie in self._get_series(): - serie_key = (serie.get('name', None), serie.get('tags', None)) + serie_key = (serie.get('name', 'results'), serie.get('tags', None)) items.append( (serie_key, self[serie_key]) ) diff --git a/tests/influxdb/client_test.py b/tests/influxdb/client_test.py index 14c3aa8d..c9bfbb26 100644 --- a/tests/influxdb/client_test.py +++ b/tests/influxdb/client_test.py @@ -266,12 +266,10 @@ def test_query(self): text=example_response ) rs = self.cli.query('select * from foo') + self.assertListEqual( - list(rs), - [{'cpu_load_short': - [{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}], - 'sdfsdfsdf': - [{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}]}] + list(rs['cpu_load_short']), + [{'value': 0.64, 'time': u'2009-11-10T23:00:00Z'}] ) @unittest.skip('Not implemented for 0.9') @@ -350,14 +348,18 @@ def test_drop_database_fails(self): cli.drop_database('old_db') def test_get_list_database(self): - data = {'results': [{'series': [ - {'name': 'databases', 'columns': ['name'], - 'values': [['mydb'], ['myotherdb']]}]}]} + data = {'results': [ + {'series': [ + {'values': [ + ['new_db_1'], + ['new_db_2']], + 'columns': ['name']}]} + ]} with _mocked_session(self.cli, 'get', 200, json.dumps(data)): self.assertListEqual( self.cli.get_list_database(), - ['mydb', 'myotherdb'] + [{'name': 'new_db_1'}, {'name': 'new_db_2'}] ) @raises(Exception) @@ -381,8 +383,10 @@ def test_get_list_series(self): self.assertListEqual( self.cli.get_list_series(), - [{'duration': '24h0m0s', - 'name': 'fsfdsdf', 'replicaN': 2}] + [{'name': 'cpu_load_short', + 'tags': [ + {'host': 'server01', '_id': 1, 'region': u'us-west'} + ]}] ) def test_create_retention_policy_default(self): diff --git a/tests/influxdb/client_test_with_server.py b/tests/influxdb/client_test_with_server.py index 0961455f..fc016398 100644 --- a/tests/influxdb/client_test_with_server.py +++ b/tests/influxdb/client_test_with_server.py @@ -11,12 +11,9 @@ """ from __future__ import print_function -import random -from collections import OrderedDict import datetime import distutils.spawn from functools import partial -import itertools import os import re import shutil @@ -289,7 +286,7 @@ def test_create_database(self): self.assertIsNone(self.cli.create_database('new_db_2')) self.assertEqual( self.cli.get_list_database(), - ['new_db_1', 'new_db_2'] + [{'name': 'new_db_1'}, {'name': 'new_db_2'}] ) def test_create_database_fails(self): @@ -303,7 +300,7 @@ def test_create_database_fails(self): def test_drop_database(self): self.test_create_database() self.assertIsNone(self.cli.drop_database('new_db_1')) - self.assertEqual(['new_db_2'], self.cli.get_list_database()) + self.assertEqual([{'name': 'new_db_2'}], self.cli.get_list_database()) def test_drop_database_fails(self): with self.assertRaises(InfluxDBClientError) as ctx: @@ -352,28 +349,27 @@ def test_write_check_read(self): ) def test_write_points(self): - ''' same as test_write() but with write_points \o/ ''' + """ same as test_write() but with write_points \o/ """ self.assertIs(True, self.cli.write_points(dummy_point)) def test_write_points_check_read(self): - ''' same as test_write_check_read() but with write_points \o/ ''' + """ same as test_write_check_read() but with write_points \o/ """ self.test_write_points() time.sleep(1) # same as test_write_check_read() rsp = self.cli.query('SELECT * FROM cpu_load_short') + self.assertEqual( - [{'values': [['2009-11-10T23:00:00Z', 0.64]], - 'name': 'cpu_load_short', - 'columns': ['time', 'value']}], - list(rsp) - ) + list(rsp), + [[{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}]] + ) rsp2 = list(rsp['cpu_load_short']) self.assertEqual(len(rsp2), 1) pt = rsp2[0] self.assertEqual( - ['cpu_load_short', ['time', 'value'], {}, ['2009-11-10T23:00:00Z', 0.64]], - [pt.serie, pt.columns, pt.tags, [pt.values.time, pt.values.value]] + pt, + {'time': '2009-11-10T23:00:00Z', 'value': 0.64} ) def test_write_multiple_points_different_series(self): @@ -381,18 +377,18 @@ def test_write_multiple_points_different_series(self): time.sleep(1) rsp = self.cli.query('SELECT * FROM cpu_load_short') lrsp = list(rsp) + self.assertEqual( - [{'values': [['2009-11-10T23:00:00Z', 0.64]], - 'name': 'cpu_load_short', - 'columns': ['time', 'value']}], - lrsp) + [[{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}]], + lrsp + ) + rsp = list(self.cli.query('SELECT * FROM memory')) + self.assertEqual( - [{ - 'values': [['2009-11-10T23:01:35Z', 33]], - 'name': 'memory', 'columns': ['time', 'value']}], - rsp - ) + rsp, + [[{'value': 33, 'time': '2009-11-10T23:01:35Z'}]] + ) @unittest.skip('Not implemented for 0.9') def test_write_points_batch(self): @@ -437,7 +433,6 @@ def test_write_points_with_precision(self): ('u', base_s_regex + '\.\d{6}Z', 1), ('ms', base_s_regex + '\.\d{3}Z', 1), ('s', base_s_regex + 'Z', 1), - ('m', base_regex + '\d{2}:00Z', 60), # ('h', base_regex + '00:00Z', ), # that would require a sleep of possibly up to 3600 secs (/ 2 ?).. @@ -475,17 +470,20 @@ def test_write_points_with_precision(self): else: pass # sys.stderr.write('ok !\n') - sleep_time = 0 + + # sys.stderr.write('sleeping %s..\n' % sleep_time) if sleep_time: - # sys.stderr.write('sleeping %s..\n' % sleep_time) time.sleep(sleep_time) rsp = self.cli.query('SELECT * FROM cpu_load_short', database=db) - rsp = list(rsp)[0] # sys.stderr.write('precision=%s rsp_timestamp = %r\n' % ( # precision, rsp['cpu_load_short'][0]['time'])) - m = re.match(expected_regex, rsp['cpu_load_short'][0]['time']) + + m = re.match( + expected_regex, + list(rsp['cpu_load_short'])[0]['time'] + ) self.assertIsNotNone(m) self.cli.drop_database(db) @@ -521,22 +519,24 @@ def test_get_list_series_empty(self): def test_get_list_series_non_empty(self): self.cli.write_points(dummy_point) rsp = self.cli.get_list_series() - self.assertEqual([ - { - 'serie_name': 'cpu_load_short', - '_id': 1, - "tags": { - "host": "server01", - "region": "us-west" - } - }], - rsp) + + self.assertEqual( + [ + {'name': 'cpu_load_short', + 'tags': [{u'host': u'server01', u'_id': 1, + u'region': u'us-west'}]} + ], + rsp + ) def test_default_retention_policy(self): rsp = self.cli.get_list_retention_policies() self.assertEqual( [ - {'name': 'default', 'duration': '0', 'replicaN': 1, 'default': True} + {'name': 'default', + 'duration': '0', + 'replicaN': 1, + 'default': True} ], rsp ) @@ -545,10 +545,14 @@ def test_create_retention_policy_default(self): self.cli.create_retention_policy('somename', '1d', 4, default=True) self.cli.create_retention_policy('another', '2d', 3, default=False) rsp = self.cli.get_list_retention_policies() - self.assertEqual([ - {'name': 'somename', 'duration': '1d', 'replicaN': 4, 'default': True}, - {'name': 'another', 'duration': '2d', 'replicaN': 3, 'default': False} - ], + + self.assertEqual( + [{'duration': '48h0m0s', 'default': False, + 'replicaN': 3, 'name': 'another'}, + {'duration': '0', 'default': False, + 'replicaN': 1, 'name': 'default'}, + {'duration': '24h0m0s', 'default': True, + 'replicaN': 4, 'name': 'somename'}], rsp ) @@ -556,9 +560,10 @@ def test_create_retention_policy(self): self.cli.create_retention_policy('somename', '1d', 4) rsp = self.cli.get_list_retention_policies() self.assertEqual( - [ - {'name': 'somename', 'duration': '1d', 'replicaN': 4, 'default': False}, - ], + [{'duration': '0', 'default': True, + 'replicaN': 1, 'name': 'default'}, + {'duration': '24h0m0s', 'default': False, + 'replicaN': 4, 'name': 'somename'}], rsp ) @@ -572,18 +577,13 @@ def test_issue_143(self): self.cli.write_points(pts) time.sleep(1) rsp = list(self.cli.query('SELECT * FROM a_serie_name GROUP BY tag_1')) - # print(rsp, file=sys.stderr) - - self.assertEqual([ - {'name': 'a_serie_name', 'columns': ['time', 'value'], - 'values': [['2015-03-30T16:16:37Z', 15]], - 'tags': {'tag_1': ''}}, - {'name': 'a_serie_name', 'columns': ['time', 'value'], - 'values': [['2015-03-30T16:16:37Z', 5]], - 'tags': {'tag_1': 'value1'}}, - {'name': 'a_serie_name', 'columns': ['time', 'value'], - 'values': [['2015-03-30T16:16:37Z', 10]], - 'tags': {'tag_1': 'value2'}}], + + self.assertEqual( + [ + [{'value': 15, 'time': '2015-03-30T16:16:37Z'}], + [{'value': 5, 'time': '2015-03-30T16:16:37Z'}], + [{'value': 10, 'time': '2015-03-30T16:16:37Z'}] + ], rsp ) @@ -597,119 +597,38 @@ def test_issue_143(self): self.cli.write_points(pts) time.sleep(1) rsp = self.cli.query('SELECT * FROM serie2 GROUP BY tag1,tag2') - # print(rsp, file=sys.stderr) - self.assertEqual([ - {'name': 'serie2', 'columns': ['time', 'value'], - 'values': [['2015-03-30T16:16:37Z', 0]], - 'tags': {'tag2': 'v1', 'tag1': 'value1'}}, - {'name': 'serie2', 'columns': ['time', 'value'], - 'values': [['2015-03-30T16:16:37Z', 5]], - 'tags': {'tag2': 'v2', 'tag1': 'value1'}}, - {'name': 'serie2', 'columns': ['time', 'value'], - 'values': [['2015-03-30T16:16:37Z', 10]], - 'tags': {'tag2': 'v1', 'tag1': 'value2'}}], - list(rsp) - ) - d = all_tag2_equal_v1 = list(rsp[None, {'tag2': 'v1'}]) self.assertEqual( [ - 2, - ['time', 'value'], - {'tag2': 'v1', 'tag1': 'value1'}, - ['2015-03-30T16:16:37Z', 0], - {'tag2': 'v1', 'tag1': 'value2'}, - ['2015-03-30T16:16:37Z', 10] + [{'value': 0, 'time': '2015-03-30T16:16:37Z'}], + [{'value': 5, 'time': '2015-03-30T16:16:37Z'}], + [{'value': 10, 'time': '2015-03-30T16:16:37Z'}] ], - [ - len(d), - d[0].columns, - d[0].tags, [d[0].values.time, d[0].values.value], - d[1].tags, [d[1].values.time, d[1].values.value] - ] + list(rsp) ) + all_tag2_equal_v1 = list(rsp[None, {'tag2': 'v1'}]) - def test_tags_json_order(self): - n_pts = 100 - n_tags = 5 # that will make 120 possible orders (fact(5) == 120) - all_tags = ['tag%s' % i for i in range(n_tags)] - n_tags_values = 1 + n_tags // 3 - all_tags_values = ['value%s' % random.randint(0, i) - for i in range(n_tags_values)] - pt = partial(point, 'serie', timestamp='2015-03-30T16:16:37Z') - pts = [ - pt(value=random.randint(0, 100)) - for _ in range(n_pts) - ] - for pt in pts: - tags = pt['tags'] = {} - for tag in all_tags: - tags[tag] = random.choice(all_tags_values) - - self.cli.write_points(pts) - time.sleep(1) - - # Influxd, when queried with a "group by tag1(, tag2, ..)" and as far - # as we currently see, always returns the tags (alphabetically-) - # ordered by their name in the json response.. - # That might not always be the case so here we will also be - # asserting that behavior. - expected_ordered_tags = tuple(sorted(all_tags)) - - # try all the possible orders of tags for the group by in the query: - for tags in itertools.permutations(all_tags): - query = ('SELECT * FROM serie ' - 'GROUP BY %s' % ','.join(tags)) - rsp = self.cli.query(query) - # and verify that, for each "serie_key" in the response, - # the tags names are ordered as we expect it: - for serie_key in rsp: - # first also asserts that the serie key is a 2-tuple: - self.assertTrue(isinstance(serie_key, tuple)) - self.assertEqual(2, len(serie_key)) - # also assert that the first component is an str instance: - self.assertIsInstance(serie_key[0], type(b''.decode())) - self.assertIsInstance(serie_key[1], tuple) - # also assert that the number of items in the second component - # is the number of tags requested in the group by actually, - # and that each one has correct format/type/.. - self.assertEqual(n_tags, len(serie_key[1])) - for tag_data in serie_key[1]: - self.assertIsInstance(tag_data, tuple) - self.assertEqual(2, len(tag_data)) - tag_name = tag_data[0] - self.assertIsInstance(tag_name, type(b''.decode())) - # then check the tags order: - rsp_tags = tuple(t[0] for t in serie_key[1]) - self.assertEqual(expected_ordered_tags, rsp_tags) - + self.assertEqual( + [{'value': 0, 'time': '2015-03-30T16:16:37Z'}, + {'value': 10, 'time': '2015-03-30T16:16:37Z'}], + all_tag2_equal_v1, + ) def test_query_multiple_series(self): pt = partial(point, 'serie1', timestamp='2015-03-30T16:16:37Z') pts = [ pt(tags={'tag1': 'value1', 'tag2': 'v1'}, value=0), - #pt(tags={'tag1': 'value1', 'tag2': 'v2'}, value=5), - #pt(tags={'tag1': 'value2', 'tag2': 'v1'}, value=10), ] self.cli.write_points(pts) pt = partial(point, 'serie2', timestamp='1970-03-30T16:16:37Z') pts = [ - pt(tags={'tag1': 'value1', 'tag2': 'v1'}, value=0, data1=33, data2="bla"), - #pt(tags={'tag1': 'value1', 'tag2': 'v2'}, value=5), - #pt(tags={'tag1': 'value2', 'tag2': 'v3'}, value=10), # data2="what"), + pt(tags={'tag1': 'value1', 'tag2': 'v1'}, + value=0, data1=33, data2="bla"), ] self.cli.write_points(pts) - rsp = self.cli.query('SELECT * FROM serie1; SELECT * FROM serie2') - print(rsp) - - # same but with the tags given : - #rsp = self.cli.query('SELECT * FROM serie1, serie2 GROUP BY *') - print(rsp) - - ############################################################################ @@ -738,7 +657,6 @@ def test_write_points_udp(self): self.assertEqual( # this is dummy_points : - {'cpu_load_short': [ - {'value': 0.64, 'time': '2009-11-10T23:00:00Z'}]}, - rsp + [{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}], + list(rsp['cpu_load_short']) ) diff --git a/tests/influxdb/resultset_test.py b/tests/influxdb/resultset_test.py index 00872f15..7c7b1650 100644 --- a/tests/influxdb/resultset_test.py +++ b/tests/influxdb/resultset_test.py @@ -16,21 +16,21 @@ def setUp(self): "columns": ["time", "value"], "values": [ ["2015-01-29T21:51:28.968422294Z", 0.64] - ]}, + ]}, {"name": "cpu_load_short", "tags": {"host": "server02", "region": "us-west"}, "columns": ["time", "value"], "values": [ ["2015-01-29T21:51:28.968422294Z", 0.64] - ]}, + ]}, {"name": "other_serie", "tags": {"host": "server01", "region": "us-west"}, "columns": ["time", "value"], "values": [ ["2015-01-29T21:51:28.968422294Z", 0.64] - ]}]} + ]}]} ] } self.rs = ResultSet(self.query_response) @@ -59,7 +59,7 @@ def test_filter_by_tags(self): ) def test_keys(self): - self.assertItemsEqual( + self.assertEqual( self.rs.keys(), [ ('cpu_load_short', {'host': 'server01', 'region': 'us-west'}), @@ -82,15 +82,18 @@ def test_items(self): items_lists, [ ( - ('cpu_load_short', {'host': 'server01', 'region': 'us-west'}), + ('cpu_load_short', + {'host': 'server01', 'region': 'us-west'}), [{'value': 0.64, 'time': '2015-01-29T21:51:28.968422294Z'}] ), ( - ('cpu_load_short', {'host': 'server02', 'region': 'us-west'}), + ('cpu_load_short', + {'host': 'server02', 'region': 'us-west'}), [{'value': 0.64, 'time': '2015-01-29T21:51:28.968422294Z'}] ), ( - ('other_serie', {'host': 'server01', 'region': 'us-west'}), + ('other_serie', + {'host': 'server01', 'region': 'us-west'}), [{'value': 0.64, 'time': '2015-01-29T21:51:28.968422294Z'}] ) ] @@ -105,3 +108,33 @@ def test_point_from_cols_vals(self): point, {'col1': 1, 'col2': '2'} ) + + def test_system_query(self): + rs = ResultSet( + {u'results': [ + {u'series': [ + {u'values': [[u'another', u'48h0m0s', 3, False], + [u'default', u'0', 1, False], + [u'somename', u'24h0m0s', 4, True]], + u'columns': [u'name', u'duration', + u'replicaN', u'default']}]} + ] + } + ) + + self.assertEqual( + rs.keys(), + [('results', None)] + ) + + self.assertEqual( + list(rs['results']), + [ + {'duration': u'48h0m0s', u'default': False, u'replicaN': 3, + u'name': u'another'}, + {u'duration': u'0', u'default': False, u'replicaN': 1, + u'name': u'default'}, + {u'duration': u'24h0m0s', u'default': True, u'replicaN': 4, + u'name': u'somename'} + ] + ) From 2402662d6f7e2cc9467d988f2f4d6f389c04c42b Mon Sep 17 00:00:00 2001 From: aviau Date: Wed, 8 Apr 2015 16:03:51 -0400 Subject: [PATCH 25/26] SHOW DATABASES now returns a databases serie --- influxdb/client.py | 2 +- tests/influxdb/client_test.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index 4438b091..da14887d 100755 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -301,7 +301,7 @@ def get_list_database(self): """ Get the list of databases """ - return list(self.query("SHOW DATABASES")['results']) + return list(self.query("SHOW DATABASES")['databases']) def create_database(self, dbname): """ diff --git a/tests/influxdb/client_test.py b/tests/influxdb/client_test.py index c9bfbb26..f2cca2f0 100644 --- a/tests/influxdb/client_test.py +++ b/tests/influxdb/client_test.py @@ -350,9 +350,10 @@ def test_drop_database_fails(self): def test_get_list_database(self): data = {'results': [ {'series': [ - {'values': [ - ['new_db_1'], - ['new_db_2']], + {'name': 'databases', + 'values': [ + ['new_db_1'], + ['new_db_2']], 'columns': ['name']}]} ]} From b9430f9ad6369cab8561fef392cfaebae835335b Mon Sep 17 00:00:00 2001 From: aviau Date: Wed, 8 Apr 2015 19:00:43 -0400 Subject: [PATCH 26/26] Python3.2 fixes --- influxdb/resultset.py | 6 ++++-- tests/influxdb/client_test.py | 4 ++-- tests/influxdb/client_test_with_server.py | 4 ++-- tests/influxdb/resultset_test.py | 26 +++++++++++------------ 4 files changed, 21 insertions(+), 19 deletions(-) diff --git a/influxdb/resultset.py b/influxdb/resultset.py index 1a0cd0b5..9be87020 100644 --- a/influxdb/resultset.py +++ b/influxdb/resultset.py @@ -30,8 +30,10 @@ def __getitem__(self, key): else: name = key tags = None - if not isinstance(name, (str, type(None), type(u''))): - raise TypeError('serie_name must be an str or None') + # TODO(aviau): Fix for python 3.2 + # if not isinstance(name, (str, bytes, type(None))) \ + # and not isinstance(name, type("".decode("utf-8"))): + # raise TypeError('serie_name must be an str or None') for serie in self._get_series(): serie_name = serie.get('name', 'results') diff --git a/tests/influxdb/client_test.py b/tests/influxdb/client_test.py index f2cca2f0..9bb8bec8 100644 --- a/tests/influxdb/client_test.py +++ b/tests/influxdb/client_test.py @@ -269,7 +269,7 @@ def test_query(self): self.assertListEqual( list(rs['cpu_load_short']), - [{'value': 0.64, 'time': u'2009-11-10T23:00:00Z'}] + [{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}] ) @unittest.skip('Not implemented for 0.9') @@ -386,7 +386,7 @@ def test_get_list_series(self): self.cli.get_list_series(), [{'name': 'cpu_load_short', 'tags': [ - {'host': 'server01', '_id': 1, 'region': u'us-west'} + {'host': 'server01', '_id': 1, 'region': 'us-west'} ]}] ) diff --git a/tests/influxdb/client_test_with_server.py b/tests/influxdb/client_test_with_server.py index fc016398..6bdbb4af 100644 --- a/tests/influxdb/client_test_with_server.py +++ b/tests/influxdb/client_test_with_server.py @@ -523,8 +523,8 @@ def test_get_list_series_non_empty(self): self.assertEqual( [ {'name': 'cpu_load_short', - 'tags': [{u'host': u'server01', u'_id': 1, - u'region': u'us-west'}]} + 'tags': [{'host': 'server01', '_id': 1, + 'region': 'us-west'}]} ], rsp ) diff --git a/tests/influxdb/resultset_test.py b/tests/influxdb/resultset_test.py index 7c7b1650..0f047c8b 100644 --- a/tests/influxdb/resultset_test.py +++ b/tests/influxdb/resultset_test.py @@ -111,13 +111,13 @@ def test_point_from_cols_vals(self): def test_system_query(self): rs = ResultSet( - {u'results': [ - {u'series': [ - {u'values': [[u'another', u'48h0m0s', 3, False], - [u'default', u'0', 1, False], - [u'somename', u'24h0m0s', 4, True]], - u'columns': [u'name', u'duration', - u'replicaN', u'default']}]} + {'results': [ + {'series': [ + {'values': [['another', '48h0m0s', 3, False], + ['default', '0', 1, False], + ['somename', '24h0m0s', 4, True]], + 'columns': ['name', 'duration', + 'replicaN', 'default']}]} ] } ) @@ -130,11 +130,11 @@ def test_system_query(self): self.assertEqual( list(rs['results']), [ - {'duration': u'48h0m0s', u'default': False, u'replicaN': 3, - u'name': u'another'}, - {u'duration': u'0', u'default': False, u'replicaN': 1, - u'name': u'default'}, - {u'duration': u'24h0m0s', u'default': True, u'replicaN': 4, - u'name': u'somename'} + {'duration': '48h0m0s', 'default': False, 'replicaN': 3, + 'name': 'another'}, + {'duration': '0', 'default': False, 'replicaN': 1, + 'name': 'default'}, + {'duration': '24h0m0s', 'default': True, 'replicaN': 4, + 'name': 'somename'} ] )