From a71ebcb1f2a855be0c5675c38a275a0835887c88 Mon Sep 17 00:00:00 2001 From: NotSqrt Date: Mon, 28 Nov 2016 13:26:27 +0100 Subject: [PATCH 01/27] Fix method name in example --- examples/tutorial_pandas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tutorial_pandas.py b/examples/tutorial_pandas.py index c9a09fde..9b3a6da5 100644 --- a/examples/tutorial_pandas.py +++ b/examples/tutorial_pandas.py @@ -29,7 +29,7 @@ def main(host='localhost', port=8086): client.query("select * from demo") print("Delete database: " + dbname) - client.delete_database(dbname) + client.drop_database(dbname) def parse_args(): From 21d025e913e8e187496c67cfb878c40465ad3bd0 Mon Sep 17 00:00:00 2001 From: h0bot Date: Wed, 14 Dec 2016 21:43:59 +0000 Subject: [PATCH 02/27] Sending via UDP line protocol throws ... due to an uninitialized data variable. --- influxdb/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/influxdb/client.py b/influxdb/client.py index ab9aa409..2dfd5f38 100644 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -779,7 +779,7 @@ def send_packet(self, packet, protocol='json'): if protocol == 'json': data = make_lines(packet).encode('utf-8') elif protocol == 'line': - data = ('\n'.join(data) + '\n').encode('utf-8') + data = ('\n'.join(packet) + '\n').encode('utf-8') self.udp_socket.sendto(data, (self._host, self.udp_port)) From e334519cca403ce7bedf0e96a99543671219e742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Christoffer=20Andersen?= Date: Mon, 2 Jan 2017 12:52:15 +0100 Subject: [PATCH 03/27] Space padded select-queries now return DataFrames Fixed an issue where space padded select-queries are interpreted as being non-select-queries thus falling back to returning a ResultSet instead of a dictionary of panda DataFrames. --- influxdb/_dataframe_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/influxdb/_dataframe_client.py b/influxdb/_dataframe_client.py index ef1fa78f..4b96250f 100644 --- a/influxdb/_dataframe_client.py +++ b/influxdb/_dataframe_client.py @@ -140,7 +140,7 @@ def query(self, query, chunked=False, database=None): """ results = super(DataFrameClient, self).query(query, database=database) - if query.upper().startswith("SELECT"): + if query.strip().upper().startswith("SELECT"): if len(results) > 0: return self._to_dataframe(results) else: From 1df262cd63204e881f00f942d6aab919e47275ff Mon Sep 17 00:00:00 2001 From: Panos Date: Thu, 16 Feb 2017 14:55:32 +0000 Subject: [PATCH 04/27] Added chunked query responses implementation and test. Added chunked parameter to client query function. --- influxdb/client.py | 16 +++++++++++++++- influxdb/tests/client_test.py | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index ab9aa409..c4f70272 100644 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -293,13 +293,23 @@ def write(self, data, params=None, expected_response_code=204, ) return True + def _read_chunked_response(self, response, raise_errors=True): + for line in response.iter_lines(): + # import ipdb; ipdb.set_trace() + if isinstance(line, bytes): + line = line.decode('utf-8') + data = json.loads(line) + for result in data.get('results', []): + yield ResultSet(result, raise_errors=raise_errors) + def query(self, query, params=None, epoch=None, expected_response_code=200, database=None, - raise_errors=True): + raise_errors=True, + chunked=False): """Send a query to InfluxDB. :param query: the actual query string @@ -339,6 +349,10 @@ def query(self, expected_response_code=expected_response_code ) + if chunked or 'chunked' in params: + params['chunked'] = 'true' + return self._read_chunked_response(response) + data = response.json() results = [ diff --git a/influxdb/tests/client_test.py b/influxdb/tests/client_test.py index f586df3f..def6ccb2 100644 --- a/influxdb/tests/client_test.py +++ b/influxdb/tests/client_test.py @@ -32,7 +32,7 @@ import unittest from influxdb import InfluxDBClient - +from influxdb.resultset import ResultSet def _build_response_object(status_code=200, content=""): resp = requests.Response() @@ -792,6 +792,37 @@ def test_invalid_port_fails(self): with self.assertRaises(ValueError): InfluxDBClient('host', '80/redir', 'username', 'password') + def test_chunked_response(self): + example_response = u'{"results":[{"statement_id":0,"series": ' \ + '[{"name":"cpu","columns":["fieldKey","fieldType"],"values":' \ + '[["value","integer"]]}],"partial":true}]}\n{"results":' \ + '[{"statement_id":0,"series":[{"name":"iops","columns":' \ + '["fieldKey","fieldType"],"values":[["value","integer"]]}],' \ + '"partial":true}]}\n{"results":[{"statement_id":0,"series":' \ + '[{"name":"load","columns":["fieldKey","fieldType"],"values":' \ + '[["value","integer"]]}],"partial":true}]}\n{"results":' \ + '[{"statement_id":0,"series":[{"name":"memory","columns":' \ + '["fieldKey","fieldType"],"values":[["value","integer"]]}]}]}\n' + + with requests_mock.Mocker() as m: + m.register_uri( + requests_mock.GET, + "http://localhost:8086/query", + text=example_response + ) + response = list(self.cli.query('show series limit 4 offset 0', chunked=True)) + self.assertTrue(len(response) == 4) + self.assertEqual(response[0].raw, ResultSet( + {"statement_id":0, + "series": [{"name":"cpu","columns":["fieldKey","fieldType"], + "values": [["value","integer"]]}],"partial":True} + ).raw) + self.assertEqual(response[3].raw, ResultSet( + {"statement_id":0, + "series":[{"name":"memory","columns": + ["fieldKey","fieldType"], + "values":[["value","integer"]]}]} + ).raw) class FakeClient(InfluxDBClient): From a7c963c779c00b47fd8edf7846b5a2d5d763ecb2 Mon Sep 17 00:00:00 2001 From: Panos Date: Thu, 16 Feb 2017 15:26:37 +0000 Subject: [PATCH 05/27] Pep8 changes --- influxdb/tests/client_test.py | 46 ++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/influxdb/tests/client_test.py b/influxdb/tests/client_test.py index def6ccb2..6886652c 100644 --- a/influxdb/tests/client_test.py +++ b/influxdb/tests/client_test.py @@ -34,6 +34,7 @@ from influxdb import InfluxDBClient from influxdb.resultset import ResultSet + def _build_response_object(status_code=200, content=""): resp = requests.Response() resp.status_code = status_code @@ -793,16 +794,17 @@ def test_invalid_port_fails(self): InfluxDBClient('host', '80/redir', 'username', 'password') def test_chunked_response(self): - example_response = u'{"results":[{"statement_id":0,"series": ' \ - '[{"name":"cpu","columns":["fieldKey","fieldType"],"values":' \ - '[["value","integer"]]}],"partial":true}]}\n{"results":' \ - '[{"statement_id":0,"series":[{"name":"iops","columns":' \ - '["fieldKey","fieldType"],"values":[["value","integer"]]}],' \ - '"partial":true}]}\n{"results":[{"statement_id":0,"series":' \ - '[{"name":"load","columns":["fieldKey","fieldType"],"values":' \ - '[["value","integer"]]}],"partial":true}]}\n{"results":' \ - '[{"statement_id":0,"series":[{"name":"memory","columns":' \ - '["fieldKey","fieldType"],"values":[["value","integer"]]}]}]}\n' + example_response = \ + u'{"results":[{"statement_id":0,"series":' \ + '[{"name":"cpu","columns":["fieldKey","fieldType"],"values":' \ + '[["value","integer"]]}],"partial":true}]}\n{"results":' \ + '[{"statement_id":0,"series":[{"name":"iops","columns":' \ + '["fieldKey","fieldType"],"values":[["value","integer"]]}],' \ + '"partial":true}]}\n{"results":[{"statement_id":0,"series":' \ + '[{"name":"load","columns":["fieldKey","fieldType"],"values":' \ + '[["value","integer"]]}],"partial":true}]}\n{"results":' \ + '[{"statement_id":0,"series":[{"name":"memory","columns":' \ + '["fieldKey","fieldType"],"values":[["value","integer"]]}]}]}\n' with requests_mock.Mocker() as m: m.register_uri( @@ -810,19 +812,23 @@ def test_chunked_response(self): "http://localhost:8086/query", text=example_response ) - response = list(self.cli.query('show series limit 4 offset 0', chunked=True)) + response = list(self.cli.query('show series limit 4 offset 0', + chunked=True)) self.assertTrue(len(response) == 4) self.assertEqual(response[0].raw, ResultSet( - {"statement_id":0, - "series": [{"name":"cpu","columns":["fieldKey","fieldType"], - "values": [["value","integer"]]}],"partial":True} - ).raw) + {"statement_id": 0, + "series": [{"name": "cpu", + "columns": ["fieldKey", "fieldType"], + "values": [["value", "integer"]]}], + "partial": True} + ).raw) self.assertEqual(response[3].raw, ResultSet( - {"statement_id":0, - "series":[{"name":"memory","columns": - ["fieldKey","fieldType"], - "values":[["value","integer"]]}]} - ).raw) + {"statement_id": 0, + "series": [{"name": "memory", + "columns": ["fieldKey", "fieldType"], + "values": [["value", "integer"]]}]} + ).raw) + class FakeClient(InfluxDBClient): From d08be8523ad9459d3692fe029f51a1abb24ebbae Mon Sep 17 00:00:00 2001 From: Panos Date: Thu, 16 Feb 2017 15:26:54 +0000 Subject: [PATCH 06/27] Ignore docs failures on travis --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 1dd2c78d..2259dc06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,9 @@ addons: - wget matrix: + allow_failures: + - python: 3.4 + env: TOX_ENV=docs include: - python: 2.7 env: TOX_ENV=py27 From 49698fc2572ed1c0e3b6bb196f66e90de9e683df Mon Sep 17 00:00:00 2001 From: Panos Date: Thu, 16 Feb 2017 15:30:12 +0000 Subject: [PATCH 07/27] Updated docstrings --- influxdb/client.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/influxdb/client.py b/influxdb/client.py index c4f70272..55656068 100644 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -295,7 +295,6 @@ def write(self, data, params=None, expected_response_code=204, def _read_chunked_response(self, response, raise_errors=True): for line in response.iter_lines(): - # import ipdb; ipdb.set_trace() if isinstance(line, bytes): line = line.decode('utf-8') data = json.loads(line) @@ -329,6 +328,11 @@ def query(self, returns errors, defaults to True :type raise_errors: bool + :param chunked: Enable to use chunked responses from InfluxDB. + With ``chunked`` enabled, a _generator_ of ResultSet objects + is returned as opposed to a list. + :type chunked: bool + :returns: the queried data :rtype: :class:`~.ResultSet` """ From 7dddb0baed32bf41bf0518bcfc5415849e3033da Mon Sep 17 00:00:00 2001 From: Panos Date: Thu, 16 Feb 2017 15:40:51 +0000 Subject: [PATCH 08/27] Added chunk size parameter --- influxdb/client.py | 8 +++++++- influxdb/tests/client_test.py | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index 55656068..bd85d96c 100644 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -308,7 +308,8 @@ def query(self, expected_response_code=200, database=None, raise_errors=True, - chunked=False): + chunked=False, + chunk_size=0): """Send a query to InfluxDB. :param query: the actual query string @@ -333,6 +334,9 @@ def query(self, is returned as opposed to a list. :type chunked: bool + :param chunk_size: Size of each chunk to tell InfluxDB to use. + :type chunk_size: int + :returns: the queried data :rtype: :class:`~.ResultSet` """ @@ -355,6 +359,8 @@ def query(self, if chunked or 'chunked' in params: params['chunked'] = 'true' + if chunk_size > 0: + params['chunk_size'] = chunk_size return self._read_chunked_response(response) data = response.json() diff --git a/influxdb/tests/client_test.py b/influxdb/tests/client_test.py index 6886652c..c5a87ae8 100644 --- a/influxdb/tests/client_test.py +++ b/influxdb/tests/client_test.py @@ -813,7 +813,7 @@ def test_chunked_response(self): text=example_response ) response = list(self.cli.query('show series limit 4 offset 0', - chunked=True)) + chunked=True, chunk_size=4)) self.assertTrue(len(response) == 4) self.assertEqual(response[0].raw, ResultSet( {"statement_id": 0, From ffd1af91025c18d162713b40be800b88609b16f9 Mon Sep 17 00:00:00 2001 From: Panos Date: Thu, 16 Feb 2017 16:14:04 +0000 Subject: [PATCH 09/27] Fix chunked and chunk size parameter set --- influxdb/client.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index bd85d96c..28f6ae11 100644 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -349,6 +349,11 @@ def query(self, if epoch is not None: params['epoch'] = epoch + if chunked: + params['chunked'] = 'true' + if chunk_size > 0: + params['chunk_size'] = chunk_size + response = self.request( url="query", method='GET', @@ -357,10 +362,7 @@ def query(self, expected_response_code=expected_response_code ) - if chunked or 'chunked' in params: - params['chunked'] = 'true' - if chunk_size > 0: - params['chunk_size'] = chunk_size + if chunked: return self._read_chunked_response(response) data = response.json() From 3625e4ce605ac2cbf049ae52602475de20b7e24b Mon Sep 17 00:00:00 2001 From: Panos Date: Tue, 21 Feb 2017 16:52:38 +0000 Subject: [PATCH 10/27] Make one result set per chunk for improved performance and API compatibility with non-chunked responses --- influxdb/client.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index 28f6ae11..0698c871 100644 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -294,12 +294,16 @@ def write(self, data, params=None, expected_response_code=204, return True def _read_chunked_response(self, response, raise_errors=True): + result_set = {} for line in response.iter_lines(): if isinstance(line, bytes): line = line.decode('utf-8') data = json.loads(line) for result in data.get('results', []): - yield ResultSet(result, raise_errors=raise_errors) + for _key in result: + if type(result[_key]) == list: + result_set.setdefault(_key, []).extend(result[_key]) + return ResultSet(result_set, raise_errors=raise_errors) def query(self, query, @@ -330,8 +334,8 @@ def query(self, :type raise_errors: bool :param chunked: Enable to use chunked responses from InfluxDB. - With ``chunked`` enabled, a _generator_ of ResultSet objects - is returned as opposed to a list. + With ``chunked`` enabled, one ResultSet is returned per chunk + containing all results within that chunk :type chunked: bool :param chunk_size: Size of each chunk to tell InfluxDB to use. From 0c2e1718c99f2246c5d49f10f28154f0bf61a485 Mon Sep 17 00:00:00 2001 From: Panos Date: Tue, 21 Feb 2017 17:16:35 +0000 Subject: [PATCH 11/27] Updated chunked responses test for API change --- influxdb/tests/client_test.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/influxdb/tests/client_test.py b/influxdb/tests/client_test.py index c5a87ae8..0ba04f4a 100644 --- a/influxdb/tests/client_test.py +++ b/influxdb/tests/client_test.py @@ -812,22 +812,23 @@ def test_chunked_response(self): "http://localhost:8086/query", text=example_response ) - response = list(self.cli.query('show series limit 4 offset 0', - chunked=True, chunk_size=4)) + response = self.cli.query('show series limit 4 offset 0', + chunked=True, chunk_size=4) self.assertTrue(len(response) == 4) - self.assertEqual(response[0].raw, ResultSet( - {"statement_id": 0, - "series": [{"name": "cpu", - "columns": ["fieldKey", "fieldType"], - "values": [["value", "integer"]]}], - "partial": True} - ).raw) - self.assertEqual(response[3].raw, ResultSet( - {"statement_id": 0, - "series": [{"name": "memory", - "columns": ["fieldKey", "fieldType"], - "values": [["value", "integer"]]}]} - ).raw) + self.assertEqual(response.__repr__(), ResultSet( + {'series': [{'values': [['value', 'integer']], + 'name': 'cpu', + 'columns': ['fieldKey', 'fieldType']}, + {'values': [['value', 'integer']], + 'name': 'iops', + 'columns': ['fieldKey', 'fieldType']}, + {'values': [['value', 'integer']], + 'name': 'load', + 'columns': ['fieldKey', 'fieldType']}, + {'values': [['value', 'integer']], + 'name': 'memory', + 'columns': ['fieldKey', 'fieldType']}]} + ).__repr__()) class FakeClient(InfluxDBClient): From 3da299af900167d508534a30716b60788df921b4 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 8 Mar 2017 00:25:42 +0000 Subject: [PATCH 12/27] Adding/updating docstring parameters --- influxdb/client.py | 15 +++++++++++---- influxdb/tests/helper_test.py | 4 ++-- influxdb/tests/influxdb08/helper_test.py | 8 ++++---- influxdb/tests/server_tests/base.py | 8 ++++---- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index 0698c871..7124fe6a 100644 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -205,6 +205,8 @@ def request(self, url, method='GET', params=None, data=None, :param expected_response_code: the expected response code of the request, defaults to 200 :type expected_response_code: int + :param headers: headers to add to the request + :type headers: dict :returns: the response from the request :rtype: :class:`requests.Response` :raises InfluxDBServerError: if the response code is any server error @@ -322,6 +324,11 @@ def query(self, :param params: additional parameters for the request, defaults to {} :type params: dict + :param epoch: response timestamps to be in epoch format either 'h', + 'm', 's', 'ms', 'u', or 'ns',defaults to `None` which is + RFC3339 UTC format with nanosecond precision + :type epoch: str + :param expected_response_code: the expected status code of response, defaults to 200 :type expected_response_code: int @@ -396,7 +403,7 @@ def write_points(self, :param points: the list of points to be written in the database :type points: list of dictionaries, each dictionary represents a point - :type data: (if protocol is 'json') list of dicts, where each dict + :type points: (if protocol is 'json') list of dicts, where each dict represents a point. (if protocol is 'line') sequence of line protocol strings. :param time_precision: Either 's', 'm', 'ms' or 'u', defaults to None @@ -575,7 +582,7 @@ def alter_retention_policy(self, name, database=None, :type duration: str :param replication: the new replication of the existing retention policy - :type replication: str + :type replication: int :param default: whether or not to set the modified policy as default :type default: bool @@ -704,9 +711,9 @@ def delete_series(self, database=None, measurement=None, tags=None): deleted, defaults to client's current database :type database: str :param measurement: Delete all series from a measurement - :type id: str + :type measurement: str :param tags: Delete all series that match given tags - :type id: dict + :type tags: dict """ database = database or self._database query_str = 'DROP SERIES' diff --git a/influxdb/tests/helper_test.py b/influxdb/tests/helper_test.py index 10546286..44392f80 100644 --- a/influxdb/tests/helper_test.py +++ b/influxdb/tests/helper_test.py @@ -261,9 +261,9 @@ class Meta: self.assertEqual(point2['time'], yesterday) def testInvalidHelpers(self): - ''' + """ Tests errors in invalid helpers. - ''' + """ class MissingMeta(SeriesHelper): pass diff --git a/influxdb/tests/influxdb08/helper_test.py b/influxdb/tests/influxdb08/helper_test.py index e744d1e4..c9ce311f 100644 --- a/influxdb/tests/influxdb08/helper_test.py +++ b/influxdb/tests/influxdb08/helper_test.py @@ -83,9 +83,9 @@ def testSingleSeriesName(self): 'Resetting helper did not empty datapoints.') def testSeveralSeriesNames(self): - ''' + """ Tests JSON conversion when there is only one series name. - ''' + """ TestSeriesHelper.MySeriesHelper(server_name='us.east-1', time=159) TestSeriesHelper.MySeriesHelper(server_name='fr.paris-10', time=158) TestSeriesHelper.MySeriesHelper(server_name='lu.lux', time=157) @@ -116,9 +116,9 @@ def testSeveralSeriesNames(self): 'Resetting helper did not empty datapoints.') def testInvalidHelpers(self): - ''' + """ Tests errors in invalid helpers. - ''' + """ class MissingMeta(SeriesHelper): pass diff --git a/influxdb/tests/server_tests/base.py b/influxdb/tests/server_tests/base.py index 3566d7ba..f217fce1 100644 --- a/influxdb/tests/server_tests/base.py +++ b/influxdb/tests/server_tests/base.py @@ -41,9 +41,9 @@ def _teardown_influxdb_server(inst): class SingleTestCaseWithServerMixin(object): - ''' A mixin for unittest.TestCase to start an influxdb server instance + """ A mixin for unittest.TestCase to start an influxdb server instance in a temporary directory **for each test function/case** - ''' + """ # 'influxdb_template_conf' attribute must be set # on the TestCase class or instance. @@ -53,10 +53,10 @@ class SingleTestCaseWithServerMixin(object): class ManyTestCasesWithServerMixin(object): - ''' Same than SingleTestCaseWithServerMixin + """ Same than SingleTestCaseWithServerMixin but creates a single instance for the whole class. Also pre-creates a fresh database: 'db'. - ''' + """ # 'influxdb_template_conf' attribute must be set on the class itself ! From 1a7e0fa54c54540fbd0e55108f82074a9a8b6b73 Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 8 Mar 2017 00:33:53 +0000 Subject: [PATCH 13/27] Removed mutable default arguments --- influxdb/_dataframe_client.py | 4 ++-- influxdb/tests/client_test.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/influxdb/_dataframe_client.py b/influxdb/_dataframe_client.py index ef1fa78f..f8fcc949 100644 --- a/influxdb/_dataframe_client.py +++ b/influxdb/_dataframe_client.py @@ -227,7 +227,7 @@ def _convert_dataframe_to_lines(self, measurement, field_columns=None, tag_columns=None, - global_tags={}, + global_tags=None, time_precision=None, numeric_precision=None): @@ -366,7 +366,7 @@ def _stringify_dataframe(self, if datatype == 'field': # If dealing with fields, format ints and strings correctly - dataframe[int_columns] = dataframe[int_columns] + 'i' + dataframe[int_columns] += 'i' dataframe[string_columns] = '"' + dataframe[string_columns] + '"' elif datatype == 'tag': dataframe = dataframe.apply(_escape_pandas_series) diff --git a/influxdb/tests/client_test.py b/influxdb/tests/client_test.py index 0ba04f4a..e92aabdc 100644 --- a/influxdb/tests/client_test.py +++ b/influxdb/tests/client_test.py @@ -838,7 +838,7 @@ def __init__(self, *args, **kwargs): def query(self, query, - params={}, + params=None, expected_response_code=200, database=None): if query == 'Fail': From 268f2a78f8e3ec1f4fb7a1e57599b6812a148d7e Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 8 Mar 2017 01:15:26 +0000 Subject: [PATCH 14/27] Simplified comparison in client --- influxdb/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/influxdb/client.py b/influxdb/client.py index 7124fe6a..f72d6e7e 100644 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -247,7 +247,7 @@ def request(self, url, method='GET', params=None, data=None, else: raise e - if response.status_code >= 500 and response.status_code < 600: + if 500 <= response.status_code < 600: raise InfluxDBServerError(response.content) elif response.status_code == expected_response_code: return response From da39921f94a90e330c796e0ad9ff861e9fc5a8af Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Tue, 21 Mar 2017 12:05:48 -0700 Subject: [PATCH 15/27] Add correct protocol to the Pandas client @nicolajkirchhof Is this what you were talking about? --- examples/tutorial_pandas.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/tutorial_pandas.py b/examples/tutorial_pandas.py index c9a09fde..91a5634e 100644 --- a/examples/tutorial_pandas.py +++ b/examples/tutorial_pandas.py @@ -8,6 +8,7 @@ def main(host='localhost', port=8086): user = 'root' password = 'root' dbname = 'example' + protocol = 'json' client = DataFrameClient(host, port, user, password, dbname) From b322ca275b4da3e316ad64f8f91560e1584ea7bb Mon Sep 17 00:00:00 2001 From: Isil Demir Date: Tue, 21 Mar 2017 17:58:06 -0700 Subject: [PATCH 16/27] fix for lost precision on float field values --- influxdb/line_protocol.py | 10 ++++++++++ influxdb/tests/test_line_protocol.py | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/influxdb/line_protocol.py b/influxdb/line_protocol.py index 7c8c8f24..180aea31 100644 --- a/influxdb/line_protocol.py +++ b/influxdb/line_protocol.py @@ -75,12 +75,22 @@ def quote_literal(value): ) +def _is_float(value): + try: + float(value) + except ValueError: + return False + return True + + def _escape_value(value): value = _get_unicode(value) if isinstance(value, text_type) and value != '': return quote_ident(value) elif isinstance(value, integer_types) and not isinstance(value, bool): return str(value) + 'i' + elif _is_float(value): + return repr(value) else: return str(value) diff --git a/influxdb/tests/test_line_protocol.py b/influxdb/tests/test_line_protocol.py index 726f8705..fc45971e 100644 --- a/influxdb/tests/test_line_protocol.py +++ b/influxdb/tests/test_line_protocol.py @@ -119,3 +119,19 @@ def test_quote_literal(self): line_protocol.quote_literal(r"""\foo ' bar " Örf"""), r"""'\\foo \' bar " Örf'""" ) + + def test_float_with_long_decimal_fraction(self): + data = { + "points": [ + { + "measurement": "test", + "fields": { + "float_val": 1.0000000000000009, + } + } + ] + } + self.assertEqual( + line_protocol.make_lines(data), + 'test float_val=1.0000000000000009\n' + ) From 889ef09125a5f1b2dc39e6e42f5532c98d44a746 Mon Sep 17 00:00:00 2001 From: nicolajkirchhof Date: Wed, 22 Mar 2017 19:17:37 +0100 Subject: [PATCH 17/27] Corrected example Tested against influxdb-python version 4.0.0 --- examples/tutorial_pandas.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/tutorial_pandas.py b/examples/tutorial_pandas.py index 91a5634e..86dc48f7 100644 --- a/examples/tutorial_pandas.py +++ b/examples/tutorial_pandas.py @@ -5,9 +5,12 @@ def main(host='localhost', port=8086): + host='localhost' + port=8086 user = 'root' password = 'root' - dbname = 'example' + dbname = 'demo' + # Temporarily used to avoid line protocol time conversion issues #412, #426, #431. protocol = 'json' client = DataFrameClient(host, port, user, password, dbname) @@ -21,16 +24,16 @@ def main(host='localhost', port=8086): client.create_database(dbname) print("Write DataFrame") - client.write_points(df, 'demo') + client.write_points(df, 'demo', protocol=protocol) print("Write DataFrame with Tags") - client.write_points(df, 'demo', {'k1': 'v1', 'k2': 'v2'}) + client.write_points(df, 'demo', {'k1': 'v1', 'k2': 'v2'}, protocol=protocol) print("Read DataFrame") client.query("select * from demo") print("Delete database: " + dbname) - client.delete_database(dbname) + client.drop_database(dbname) def parse_args(): From 449d02eb1b5e2a532d3c323f09195242cf44f2b8 Mon Sep 17 00:00:00 2001 From: nicolajkirchhof Date: Wed, 22 Mar 2017 19:23:58 +0100 Subject: [PATCH 18/27] Removed obsolete code --- examples/tutorial_pandas.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/tutorial_pandas.py b/examples/tutorial_pandas.py index 86dc48f7..855f5740 100644 --- a/examples/tutorial_pandas.py +++ b/examples/tutorial_pandas.py @@ -5,8 +5,6 @@ def main(host='localhost', port=8086): - host='localhost' - port=8086 user = 'root' password = 'root' dbname = 'demo' From 99a5a65cc82f908a84a9b8780a40e449259e4c06 Mon Sep 17 00:00:00 2001 From: Ron Rothman Date: Tue, 28 Mar 2017 12:26:31 -0400 Subject: [PATCH 19/27] reduced the number of objects allocated in make_lines. no change in functionality; only efficiency. --- influxdb/line_protocol.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/influxdb/line_protocol.py b/influxdb/line_protocol.py index 7c8c8f24..2042add4 100644 --- a/influxdb/line_protocol.py +++ b/influxdb/line_protocol.py @@ -5,13 +5,12 @@ from __future__ import print_function from __future__ import unicode_literals -from copy import copy from datetime import datetime from numbers import Integral from pytz import UTC from dateutil.parser import parse -from six import binary_type, text_type, integer_types, PY2 +from six import iteritems, binary_type, text_type, integer_types, PY2 EPOCH = UTC.localize(datetime.utcfromtimestamp(0)) @@ -108,7 +107,7 @@ def make_lines(data, precision=None): matching the line protocol introduced in InfluxDB 0.9.0. """ lines = [] - static_tags = data.get('tags', None) + static_tags = data.get('tags') for point in data['points']: elements = [] @@ -119,32 +118,29 @@ def make_lines(data, precision=None): key_values = [measurement] # add tags - if static_tags is None: - tags = point.get('tags', {}) + if static_tags: + tags = dict(static_tags) # make a copy, since we'll modify + tags.update(point.get('tags') or {}) else: - tags = copy(static_tags) - tags.update(point.get('tags', {})) + tags = point.get('tags') or {} # tags should be sorted client-side to take load off server - for tag_key in sorted(tags.keys()): + for tag_key, tag_value in sorted(iteritems(tags)): key = _escape_tag(tag_key) - value = _escape_tag(tags[tag_key]) + value = _escape_tag(tag_value) if key != '' and value != '': - key_values.append("{key}={value}".format(key=key, value=value)) + key_values.append(key + "=" + value) key_values = ','.join(key_values) elements.append(key_values) # add fields field_values = [] - for field_key in sorted(point['fields'].keys()): + for field_key, field_value in sorted(iteritems(point['fields'])): key = _escape_tag(field_key) - value = _escape_value(point['fields'][field_key]) + value = _escape_value(field_value) if key != '' and value != '': - field_values.append("{key}={value}".format( - key=key, - value=value - )) + field_values.append(key + "=" + value) field_values = ','.join(field_values) elements.append(field_values) From eb049f197e76575e53ff883ecd2eff05c519da00 Mon Sep 17 00:00:00 2001 From: Isil Demir Date: Wed, 5 Apr 2017 17:14:46 -0700 Subject: [PATCH 20/27] docstring for pep257 compliance --- influxdb/tests/test_line_protocol.py | 1 + 1 file changed, 1 insertion(+) diff --git a/influxdb/tests/test_line_protocol.py b/influxdb/tests/test_line_protocol.py index fc45971e..61f54557 100644 --- a/influxdb/tests/test_line_protocol.py +++ b/influxdb/tests/test_line_protocol.py @@ -121,6 +121,7 @@ def test_quote_literal(self): ) def test_float_with_long_decimal_fraction(self): + """Ensure precision is preserved when casting floats into strings.""" data = { "points": [ { From 194ef13cf802854eab4c6d2accaa6c81c45239d1 Mon Sep 17 00:00:00 2001 From: Matthew McGinn Date: Sun, 9 Apr 2017 21:51:53 -0400 Subject: [PATCH 21/27] Allow user-configurable number of retries when submitting request to database --- influxdb/client.py | 19 ++++++++++++------- influxdb/influxdb08/client.py | 21 +++++++++++++-------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index f72d6e7e..05245e7b 100644 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -53,6 +53,9 @@ class InfluxDBClient(object): :param timeout: number of seconds Requests will wait for your client to establish a connection, defaults to None :type timeout: int + :param retries: number of retries your client will try before aborting, + defaults to 3. 0 indicates try until success + :type retries: int :param use_udp: use UDP to connect to InfluxDB, defaults to False :type use_udp: bool :param udp_port: UDP port to connect to InfluxDB, defaults to 4444 @@ -70,6 +73,7 @@ def __init__(self, ssl=False, verify_ssl=False, timeout=None, + retries=3, use_udp=False, udp_port=4444, proxies=None, @@ -81,6 +85,7 @@ def __init__(self, self._password = password self._database = database self._timeout = timeout + self._retries = retries self._verify_ssl = verify_ssl @@ -225,9 +230,10 @@ def request(self, url, method='GET', params=None, data=None, if isinstance(data, (dict, list)): data = json.dumps(data) - # Try to send the request a maximum of three times. (see #103) - # TODO (aviau): Make this configurable. - for i in range(0, 3): + # Try to send the request more than once by default (see #103) + retry = True + _try = 0 + while retry: try: response = self._session.request( method=method, @@ -242,10 +248,9 @@ def request(self, url, method='GET', params=None, data=None, ) break except requests.exceptions.ConnectionError as e: - if i < 2: - continue - else: - raise e + _try += 1 + if self._retries != 0: + retry = _try < self._retries if 500 <= response.status_code < 600: raise InfluxDBServerError(response.content) diff --git a/influxdb/influxdb08/client.py b/influxdb/influxdb08/client.py index a3b31639..7797884b 100644 --- a/influxdb/influxdb08/client.py +++ b/influxdb/influxdb08/client.py @@ -55,6 +55,9 @@ class InfluxDBClient(object): :param verify_ssl: verify SSL certificates for HTTPS requests, defaults is False :type verify_ssl: boolean + :param retries: number of retries your client will try before aborting, + defaults to 3. 0 indicates try until success + :type retries: int :param timeout: number of seconds Requests will wait for your client to establish a connection, defaults to None :type timeout: int @@ -73,6 +76,7 @@ def __init__(self, ssl=False, verify_ssl=False, timeout=None, + retries=3, use_udp=False, udp_port=4444): """ @@ -84,6 +88,7 @@ def __init__(self, self._password = password self._database = database self._timeout = timeout + self._retries = retries self._verify_ssl = verify_ssl @@ -228,10 +233,11 @@ def request(self, url, method='GET', params=None, data=None, if data is not None and not isinstance(data, str): data = json.dumps(data) - # Try to send the request a maximum of three times. (see #103) - # TODO (aviau): Make this configurable. - for i in range(0, 3): - try: + retry = True + _try = 0 + # Try to send the request more than once by default (see #103) + while retry: + try: response = session.request( method=method, url=url, @@ -244,10 +250,9 @@ def request(self, url, method='GET', params=None, data=None, break except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as e: - if i < 2: - continue - else: - raise e + _try += 1 + if self._retries != 0: + retry = _try < self._retries if response.status_code == expected_response_code: return response From 93b91e461114f87744c4b0998b30736998ff2b39 Mon Sep 17 00:00:00 2001 From: Matthew McGinn Date: Tue, 11 Apr 2017 23:39:10 -0400 Subject: [PATCH 22/27] fixing indentation, adding tests to confirm connection behavior --- influxdb/client.py | 3 ++ influxdb/influxdb08/client.py | 6 ++-- influxdb/tests/client_test.py | 54 +++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/influxdb/client.py b/influxdb/client.py index 05245e7b..6c9ed1b9 100644 --- a/influxdb/client.py +++ b/influxdb/client.py @@ -252,6 +252,9 @@ def request(self, url, method='GET', params=None, data=None, if self._retries != 0: retry = _try < self._retries + else: + raise requests.exceptions.ConnectionError + if 500 <= response.status_code < 600: raise InfluxDBServerError(response.content) elif response.status_code == expected_response_code: diff --git a/influxdb/influxdb08/client.py b/influxdb/influxdb08/client.py index 7797884b..00cf4fe5 100644 --- a/influxdb/influxdb08/client.py +++ b/influxdb/influxdb08/client.py @@ -248,11 +248,13 @@ def request(self, url, method='GET', params=None, data=None, timeout=self._timeout ) break - except (requests.exceptions.ConnectionError, - requests.exceptions.Timeout) as e: + except (requests.exceptions.ConnectionError, + requests.exceptions.Timeout) as e: _try += 1 if self._retries != 0: retry = _try < self._retries + else: + raise requests.exceptions.ConnectionError if response.status_code == expected_response_code: return response diff --git a/influxdb/tests/client_test.py b/influxdb/tests/client_test.py index e92aabdc..f998c4e9 100644 --- a/influxdb/tests/client_test.py +++ b/influxdb/tests/client_test.py @@ -645,6 +645,60 @@ def connection_error(self, *args, **kwargs): with self.assertRaises(requests.exceptions.ConnectionError): cli.write_points(self.dummy_points) + @mock.patch('requests.Session.request') + def test_random_request_retry(self, mock_request): + """Tests that a random number of connection errors will be handled""" + + class CustomMock(object): + def __init__(self, retries): + self.i = 0 + self.retries = retries + + def connection_error(self, *args, **kwargs): + self.i += 1 + + if self.i < self.retries: + raise requests.exceptions.ConnectionError + else: + r = requests.Response() + r.status_code = 204 + return r + + retries = random.randint(1, 100) + mock_request.side_effect = CustomMock(retries).connection_error + + cli = InfluxDBClient(database='db', retries=retries) + cli.write_points( + self.dummy_points + ) + + @mock.patch('requests.Session.request') + def test_random_request_retry_raises(self, mock_request): + """Tests that a random number of connection errors plus one will be not handled""" + + class CustomMock(object): + def __init__(self, retries): + self.i = 0 + self.retries = retries + + def connection_error(self, *args, **kwargs): + self.i += 1 + + if self.i < self.retries + 1: + raise requests.exceptions.ConnectionError + else: + r = requests.Response() + r.status_code = 200 + return r + + retries = random.randint(1, 100) + mock_request.side_effect = CustomMock(retries).connection_error + + cli = InfluxDBClient(database='db', retries=retries) + + with self.assertRaises(requests.exceptions.ConnectionError): + cli.write_points(self.dummy_points) + def test_get_list_users(self): example_response = ( '{"results":[{"series":[{"columns":["user","admin"],' From 74ce6444182f4e588d77faf92a4696900458141a Mon Sep 17 00:00:00 2001 From: Matthew McGinn Date: Tue, 11 Apr 2017 23:45:19 -0400 Subject: [PATCH 23/27] fixing flake8 failures --- influxdb/influxdb08/client.py | 6 +++--- influxdb/tests/client_test.py | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/influxdb/influxdb08/client.py b/influxdb/influxdb08/client.py index 00cf4fe5..8955ab76 100644 --- a/influxdb/influxdb08/client.py +++ b/influxdb/influxdb08/client.py @@ -237,7 +237,7 @@ def request(self, url, method='GET', params=None, data=None, _try = 0 # Try to send the request more than once by default (see #103) while retry: - try: + try: response = session.request( method=method, url=url, @@ -248,8 +248,8 @@ def request(self, url, method='GET', params=None, data=None, timeout=self._timeout ) break - except (requests.exceptions.ConnectionError, - requests.exceptions.Timeout) as e: + except (requests.exceptions.ConnectionError, + requests.exceptions.Timeout) as e: _try += 1 if self._retries != 0: retry = _try < self._retries diff --git a/influxdb/tests/client_test.py b/influxdb/tests/client_test.py index f998c4e9..e319c0e5 100644 --- a/influxdb/tests/client_test.py +++ b/influxdb/tests/client_test.py @@ -674,7 +674,8 @@ def connection_error(self, *args, **kwargs): @mock.patch('requests.Session.request') def test_random_request_retry_raises(self, mock_request): - """Tests that a random number of connection errors plus one will be not handled""" + """Tests that a random number of connection errors plus one \ + will not be handled""" class CustomMock(object): def __init__(self, retries): From 262e1cf6ac40c087089f7d794e60eb8f23e9abe8 Mon Sep 17 00:00:00 2001 From: aviau Date: Wed, 12 Apr 2017 03:09:15 -0400 Subject: [PATCH 24/27] 4.1.0 tag --- influxdb/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/influxdb/__init__.py b/influxdb/__init__.py index 00739c49..90c049c9 100644 --- a/influxdb/__init__.py +++ b/influxdb/__init__.py @@ -17,4 +17,4 @@ ] -__version__ = '4.0.0' +__version__ = '4.1.0' From 87e2dfee6c23203b08fe938d63dadfdbabdc34a2 Mon Sep 17 00:00:00 2001 From: aviau Date: Wed, 12 Apr 2017 03:21:11 -0400 Subject: [PATCH 25/27] tox.ini: bump Sphinx to 1.5.5 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 856a4717..15060e37 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,7 @@ commands = nosetests -v --with-coverage --cover-html --cover-package=influxdb [testenv:docs] deps = -r{toxinidir}/requirements.txt pandas - Sphinx==1.2.3 + Sphinx==1.5.5 sphinx_rtd_theme commands = sphinx-build -b html docs/source docs/build From 9f4c7bfba857ed9e14e5a3a437cac0ae0a0b9d2c Mon Sep 17 00:00:00 2001 From: aviau Date: Wed, 12 Apr 2017 03:25:23 -0400 Subject: [PATCH 26/27] dev-requirements.txt: bump sphinx to 1.5.5 --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 78d40a24..7d75102e 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -2,7 +2,7 @@ requests nose mock pandas -Sphinx==1.2.3 +Sphinx==1.5.5 sphinx_rtd_theme wheel twine From fddea93e283cc3cbdbcbe37053ed70018dc6c122 Mon Sep 17 00:00:00 2001 From: Andrew Long Date: Mon, 15 May 2017 10:40:27 -0400 Subject: [PATCH 27/27] Fixed typo Line 44, "Queying data: " to "Querying data: " --- examples/tutorial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tutorial.py b/examples/tutorial.py index e790ece5..92ae7e3b 100644 --- a/examples/tutorial.py +++ b/examples/tutorial.py @@ -41,7 +41,7 @@ def main(host='localhost', port=8086): print("Write points: {0}".format(json_body)) client.write_points(json_body) - print("Queying data: " + query) + print("Querying data: " + query) result = client.query(query) print("Result: {0}".format(result))