Skip to content

Commit 7553d73

Browse files
author
aviau
committed
Merge branch 'master' of github.com:influxdb/influxdb-python into line-protocol
Conflicts: tests/influxdb/client_test.py
2 parents 1350d05 + 881a8c3 commit 7553d73

13 files changed

+183
-115
lines changed

README.rst

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,10 @@ InfluxDB is an open-source distributed time series database, find more about Inf
3636

3737
.. _installation:
3838

39-
InfluxDB > v0.9 support
40-
=======================
41-
42-
The 1.0.0 version of this library now supports InfluxDB 0.9. Please note that InfluxDB 0.9 is still pre-release software. For stability, you should use the ``influxdb.influxdb08`` module in conjunction with InfluxDB 0.8.
43-
44-
4539
InfluxDB v0.8.X users
4640
=====================
4741

48-
Influxdb >=0.9.0 brings many breaking changes to the API. InfluxDB 0.8.X users may use the legacy client by using ``from influxdb.influxdb08 import InfluxDBClient`` instead.
42+
InfluxDB 0.9 was released and it is the new recommended version. However, InfluxDB 0.8.x users may still use the legacy client by using ``from influxdb.influxdb08 import InfluxDBClient`` instead.
4943

5044
Installation
5145
============
@@ -109,7 +103,7 @@ Here's a basic example (for more see the examples directory)::
109103
"host": "server01",
110104
"region": "us-west"
111105
},
112-
"timestamp": "2009-11-10T23:00:00Z",
106+
"time": "2009-11-10T23:00:00Z",
113107
"fields": {
114108
"value": 0.64
115109
}

docs/source/resultset.rst

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,39 @@ Query response object: ResultSet
77

88
Using the ``InfluxDBClient.query()`` function will return a ``ResultSet`` Object.
99

10-
A ResultSet behaves like a dict. Its keys are series and values are points. However, it is a little bit smarter than a regular dict. Its ``__getitem__`` method can be used to query the ResultSet in several ways.
10+
A ResultSet can be browsed in several ways. Its ``get_points`` method can be used to retrieve points generators that filter either by measurement, tags, or both.
1111

12-
Filtering by serie name
13-
-----------------------
12+
Getting all points
13+
------------------
1414

15-
Using ``rs['cpu']`` will return a generator for all the points that are in a serie named ``cpu``, no matter the tags.
15+
Using ``rs.get_points()`` will return a generator for all the points in the ResultSet.
16+
17+
18+
Filtering by measurement
19+
------------------------
20+
21+
Using ``rs.get_points('cpu')`` will return a generator for all the points that are in a serie with measurement name ``cpu``, no matter the tags.
1622
::
1723

1824
rs = cli.query("SELECT * from cpu")
19-
cpu_points = list(rs['cpu'])
25+
cpu_points = list(rs.get_points(measurement='cpu')])
2026

2127
Filtering by tags
2228
-----------------
2329

24-
Using ``rs[{'host_name': 'influxdb.com'}]`` will return a generator for all the points that are tagged with the specified tags, no matter the serie name.
30+
Using ``rs.get_points(tags={'host_name': 'influxdb.com'})`` will return a generator for all the points that are tagged with the specified tags, no matter the measurement name.
2531
::
2632

2733
rs = cli.query("SELECT * from cpu")
28-
cpu_influxdb_com_points = list(rs[{"host_name": "influxdb.com"}])
34+
cpu_influxdb_com_points = list(rs.get_points(tags={"host_name": "influxdb.com"}))
2935

30-
Filtering by serie name and tags
31-
--------------------------------
36+
Filtering by measurement and tags
37+
---------------------------------
3238

33-
Using a tuple with a serie name and a dict will return a generator for all the points that are in a serie with the given name AND whose tags match the given tags.
39+
Using measurement name and tags will return a generator for all the points that are in a serie with the specified measurement name AND whose tags match the given tags.
3440
::
3541

3642
rs = cli.query("SELECT * from cpu")
37-
points = list(rs[('cpu', {'host_name': 'influxdb.com'})])
43+
points = list(rs.get_points(measurement='cpu', tags={'host_name': 'influxdb.com'}))
3844

3945
See the :ref:`api-documentation` page for more information.

examples/tutorial.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ def main(host='localhost', port=8086):
1212
query = 'select value from cpu_load_short;'
1313
json_body = [
1414
{
15-
"name": "cpu_load_short",
15+
"measurement": "cpu_load_short",
1616
"tags": {
1717
"host": "server01",
1818
"region": "us-west"
1919
},
20-
"timestamp": "2009-11-10T23:00:00Z",
20+
"time": "2009-11-10T23:00:00Z",
2121
"fields": {
2222
"value": 0.64
2323
}

examples/tutorial_server_data.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ def main(host='localhost', port=8086, nb_day=15):
2828
hostName = "server-%d" % random.randint(1, 5)
2929
# pointValues = [int(past_date.strftime('%s')), value, hostName]
3030
pointValues = {
31-
"timestamp": int(past_date.strftime('%s')),
32-
"name": metric,
31+
"time": int(past_date.strftime('%s')),
32+
"measurement": metric,
3333
'fields': {
3434
'value': value,
3535
},

examples/tutorial_sine_wave.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ def main(host='localhost', port=8086):
2222
y = 10 + math.sin(math.radians(angle)) * 10
2323

2424
point = {
25-
"name": 'foobar',
26-
"timestamp": int(now.strftime('%s')) + angle,
25+
"measurement": 'foobar',
26+
"time": int(now.strftime('%s')) + angle,
2727
"fields": {
2828
"value": y
2929
}

influxdb/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
]
1414

1515

16-
__version__ = '2.4.0'
16+
__version__ = '2.6.0'

influxdb/client.py

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
from influxdb.line_protocol import make_lines
1515
from influxdb.resultset import ResultSet
16+
from .exceptions import InfluxDBClientError
17+
from .exceptions import InfluxDBServerError
1618

1719
try:
1820
xrange
@@ -25,23 +27,6 @@
2527
from urlparse import urlparse
2628

2729

28-
class InfluxDBClientError(Exception):
29-
"""Raised when an error occurs in the request."""
30-
def __init__(self, content, code):
31-
if isinstance(content, type(b'')):
32-
content = content.decode('UTF-8', errors='replace')
33-
super(InfluxDBClientError, self).__init__(
34-
"{0}: {1}".format(code, content))
35-
self.content = content
36-
self.code = code
37-
38-
39-
class InfluxDBServerError(Exception):
40-
"""Raised when a server error occurs."""
41-
def __init__(self, content):
42-
super(InfluxDBServerError, self).__init__(content)
43-
44-
4530
class InfluxDBClient(object):
4631
"""The :class:`~.InfluxDBClient` object holds information necessary to
4732
connect to InfluxDB. Requests can be made to InfluxDB directly through
@@ -285,18 +270,27 @@ def query(self,
285270
query,
286271
params={},
287272
expected_response_code=200,
288-
database=None):
273+
database=None,
274+
raise_errors=True):
289275
"""Send a query to InfluxDB.
290276
291277
:param query: the actual query string
292278
:type query: str
279+
293280
:param params: additional parameters for the request, defaults to {}
294281
:type params: dict
282+
295283
:param expected_response_code: the expected status code of response,
296284
defaults to 200
297285
:type expected_response_code: int
286+
298287
:param database: database to query, defaults to None
299288
:type database: str
289+
290+
:param raise_errors: Whether or not to raise exceptions when InfluxDB
291+
returns errors, defaults to True
292+
:type raise_errors: bool
293+
300294
:returns: the queried data
301295
:rtype: :class:`~.ResultSet`
302296
"""
@@ -313,7 +307,17 @@ def query(self,
313307

314308
data = response.json()
315309

316-
return ResultSet(data)
310+
results = [
311+
ResultSet(result, raise_errors=raise_errors)
312+
for result
313+
in data.get('results', [])
314+
]
315+
316+
# TODO(aviau): Always return a list. (This would be a breaking change)
317+
if len(results) == 1:
318+
return results[0]
319+
else:
320+
return results
317321

318322
def write_points(self,
319323
points,
@@ -426,7 +430,7 @@ def get_list_database(self):
426430
>>> dbs
427431
[{u'name': u'db1'}, {u'name': u'db2'}, {u'name': u'db3'}]
428432
"""
429-
return list(self.query("SHOW DATABASES")['databases'])
433+
return list(self.query("SHOW DATABASES").get_points())
430434

431435
def create_database(self, dbname):
432436
"""Create a new database in InfluxDB.
@@ -533,7 +537,7 @@ def get_list_retention_policies(self, database=None):
533537
rsp = self.query(
534538
"SHOW RETENTION POLICIES %s" % (database or self._database)
535539
)
536-
return list(rsp['results'])
540+
return list(rsp.get_points())
537541

538542
def get_list_series(self, database=None):
539543
"""Get the list of series for a database.
@@ -578,7 +582,7 @@ def get_list_users(self):
578582
{u'admin': False, u'user': u'user2'},
579583
{u'admin': False, u'user': u'user3'}]
580584
"""
581-
return list(self.query("SHOW USERS")["results"])
585+
return list(self.query("SHOW USERS").get_points())
582586

583587
def create_user(self, username, password):
584588
"""Create a new user in InfluxDB

influxdb/exceptions.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class InfluxDBClientError(Exception):
2+
"""Raised when an error occurs in the request."""
3+
def __init__(self, content, code=None):
4+
if isinstance(content, type(b'')):
5+
content = content.decode('UTF-8', errors='replace')
6+
7+
if code is not None:
8+
message = "%s: %s" % (code, content)
9+
else:
10+
message = content
11+
12+
super(InfluxDBClientError, self).__init__(
13+
message
14+
)
15+
self.content = content
16+
self.code = code
17+
18+
19+
class InfluxDBServerError(Exception):
20+
"""Raised when a server error occurs."""
21+
def __init__(self, content):
22+
super(InfluxDBServerError, self).__init__(content)

influxdb/influxdb08/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525

2626

2727
class InfluxDBClientError(Exception):
28-
"Raised when an error occurs in the request"
29-
def __init__(self, content, code):
28+
"""Raised when an error occurs in the request"""
29+
def __init__(self, content, code=-1):
3030
super(InfluxDBClientError, self).__init__(
3131
"{0}: {1}".format(code, content))
3232
self.content = content

influxdb/resultset.py

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
11
# -*- coding: utf-8 -*-
22

3+
import warnings
4+
5+
from influxdb.exceptions import InfluxDBClientError
6+
37
_sentinel = object()
48

59

610
class ResultSet(object):
7-
"""A wrapper around series results """
11+
"""A wrapper around a single InfluxDB query result"""
812

9-
def __init__(self, series):
13+
def __init__(self, series, raise_errors=True):
1014
self._raw = series
15+
self._error = self.raw.get('error', None)
16+
17+
if self.error is not None and raise_errors is True:
18+
raise InfluxDBClientError(self.error)
1119

1220
@property
1321
def raw(self):
@@ -18,6 +26,11 @@ def raw(self):
1826
def raw(self, value):
1927
self._raw = value
2028

29+
@property
30+
def error(self):
31+
"""Error returned by InfluxDB"""
32+
return self._error
33+
2134
def __getitem__(self, key):
2235
"""
2336
:param key: Either a serie name, or a tags_dict, or
@@ -32,6 +45,13 @@ def __getitem__(self, key):
3245
The order in which the points are yielded is actually undefined but
3346
it might change..
3447
"""
48+
49+
warnings.warn(
50+
("ResultSet's ``__getitem__`` method will be deprecated. Use"
51+
"``get_points`` instead."),
52+
DeprecationWarning
53+
)
54+
3555
if isinstance(key, tuple):
3656
if 2 != len(key):
3757
raise TypeError('only 2-tuples allowed')
@@ -46,23 +66,40 @@ def __getitem__(self, key):
4666
name = key
4767
tags = None
4868

49-
if not isinstance(name, (bytes, type(b''.decode()), type(None))):
50-
raise TypeError('serie_name must be an str or None')
69+
return self.get_points(name, tags)
70+
71+
def get_points(self, measurement=None, tags=None):
72+
"""
73+
Returns a generator for all the points that match the given filters.
74+
75+
:param measurement: The measurement name
76+
:type measurement: str
77+
78+
:param tags: Tags to look for
79+
:type tags: dict
80+
81+
:return: Points generator
82+
"""
83+
84+
# Raise error if measurement is not str or bytes
85+
if not isinstance(measurement,
86+
(bytes, type(b''.decode()), type(None))):
87+
raise TypeError('measurement must be an str or None')
5188

5289
for serie in self._get_series():
5390
serie_name = serie.get('measurement', serie.get('name', 'results'))
5491
if serie_name is None:
5592
# this is a "system" query or a query which
5693
# doesn't return a name attribute.
5794
# like 'show retention policies' ..
58-
if key is None:
95+
if tags is None:
5996
for point in serie['values']:
6097
yield self.point_from_cols_vals(
6198
serie['columns'],
6299
point
63100
)
64101

65-
elif name in (None, serie_name):
102+
elif measurement in (None, serie_name):
66103
# by default if no tags was provided then
67104
# we will matches every returned serie
68105
serie_tags = serie.get('tags', {})
@@ -101,13 +138,7 @@ def _tag_matches(self, tags, filter):
101138

102139
def _get_series(self):
103140
"""Returns all series"""
104-
series = []
105-
try:
106-
for result in self.raw['results']:
107-
series.extend(result['series'])
108-
except KeyError:
109-
pass
110-
return series
141+
return self.raw.get('series', [])
111142

112143
def __len__(self):
113144
return len(self.keys())

tests/influxdb/client_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ def test_query(self):
320320
rs = self.cli.query('select * from foo')
321321

322322
self.assertListEqual(
323-
list(rs['cpu_load_short']),
323+
list(rs[0].get_points()),
324324
[{'value': 0.64, 'time': '2009-11-10T23:00:00Z'}]
325325
)
326326

0 commit comments

Comments
 (0)