Skip to content

Commit c90dd3a

Browse files
author
Christopher Rabotin
committed
Client can now be specified either in the Helper or on commit call.
Specifying in the Helper allows for autocommit. Also updated the tests for better coverage.
1 parent 38f91f2 commit c90dd3a

File tree

2 files changed

+75
-20
lines changed

2 files changed

+75
-20
lines changed

influxdb/helper.py

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,35 @@
33
Helper class for InfluxDB
44
"""
55
from collections import namedtuple, defaultdict
6+
from warnings import warn
7+
68
import six
79

10+
811
class SeriesHelper(object):
912
'''
1013
Subclassing this helper eases writing data points in bulk.
1114
All data points are immutable, insuring they do not get overwritten.
1215
Each subclass can write to its own database.
1316
The time series names can also be based on one or more defined fields.
14-
17+
1518
Annotated example:
1619
```
1720
class MySeriesHelper(SeriesHelper):
1821
class Meta:
1922
# Meta class stores time series helper configuration.
20-
client = TestSeriesHelper.client
21-
# The client should be an instance of InfluxDBClient.
2223
series_name = 'events.stats.{server_name}'
2324
# The series name must be a string. Add dependent field names in curly brackets.
2425
fields = ['time', 'server_name']
2526
# Defines all the fields in this time series.
27+
### Following attributes are optional. ###
28+
client = TestSeriesHelper.client
29+
# The client should be an instance of InfluxDBClient. Only used if autocommit is True.
2630
bulk_size = 5
27-
# Defines the number of data points to store prior to writing on the wire.
31+
# Defines the number of data points to store prior to writing on the wire. Defaults to 1.
2832
autocommit = True
29-
# Sets autocommit: must be set to True for bulk_size to have any affect.
30-
33+
# Sets autocommit: must be set to True for bulk_size to have any affect. Defaults to False.
34+
3135
# The following will create *five* (immutable) data points.
3236
# Since bulk_size is set to 5, upon the fifth construction call, *all* data
3337
# points will be written on the wire via MySeriesHelper.Meta.client.
@@ -36,11 +40,11 @@ class Meta:
3640
MySeriesHelper(server_name='us.east-1', time=157)
3741
MySeriesHelper(server_name='us.east-1', time=156)
3842
MySeriesHelper(server_name='us.east-1', time=155)
39-
43+
4044
# If autocommit unset (or set to False), one must call commit to write datapoints.
4145
# To manually submit data points which are not yet written, call commit:
4246
MySeriesHelper.commit()
43-
47+
4448
# To inspect the JSON which will be written, call _json_body_():
4549
MySeriesHelper._json_body_()
4650
```
@@ -50,22 +54,38 @@ class Meta:
5054
def __new__(cls, *args, **kwargs):
5155
'''
5256
Initializes class attributes for subsequent constructor calls.
57+
:note: *args and **kwargs are not explicitly used in this function,
58+
but needed for Python 2 compatibility.
5359
'''
5460
if not cls.__initialized__:
5561
cls.__initialized__ = True
5662
try:
5763
_meta = getattr(cls, 'Meta')
5864
except AttributeError:
5965
raise AttributeError('Missing Meta class in {}.'.format(cls.__name__))
60-
61-
for attr in ['series_name', 'fields', 'client']:
66+
67+
for attr in ['series_name', 'fields']:
6268
try:
6369
setattr(cls, '_' + attr, getattr(_meta, attr))
6470
except AttributeError:
6571
raise AttributeError('Missing {} in {} Meta class.'.format(attr, cls.__name__))
66-
72+
6773
cls._autocommit = getattr(_meta, 'autocommit', False)
68-
cls._bulk_size = getattr(_meta, 'bulk_size', 1)
74+
75+
cls._client = getattr(_meta, 'client', None)
76+
if cls._autocommit and not cls._client:
77+
raise AttributeError('In {}, autocommit is set to True, but no client is set.'.format(cls.__name__))
78+
79+
try:
80+
cls._bulk_size = getattr(_meta, 'bulk_size')
81+
if cls._bulk_size < 1 and cls._autocommit:
82+
warn('Definition of bulk_size in {} forced to 1, was less than 1.'.format(cls.__name__))
83+
cls._bulk_size = 1
84+
except AttributeError:
85+
cls._bulk_size = -1
86+
else:
87+
if not cls._autocommit:
88+
warn('Definition of bulk_size in {} has no affect because autocommit is false.'.format(cls.__name__))
6989

7090
cls._datapoints = defaultdict(list)
7191
cls._type = namedtuple(cls.__name__, cls._fields)
@@ -76,7 +96,7 @@ def __init__(self, **kw):
7696
'''
7797
Constructor call creates a new data point. All fields must be present.
7898
:note: Data points written when `bulk_size` is reached per Helper.
79-
:warning: Data points are *immutable* (`namedtuples`).
99+
:warning: Data points are *immutable* (`namedtuples`).
80100
'''
81101
cls = self.__class__
82102

@@ -89,12 +109,16 @@ def __init__(self, **kw):
89109
cls.commit()
90110

91111
@classmethod
92-
def commit(cls):
112+
def commit(cls, client=None):
93113
'''
94114
Commit everything from datapoints via the client.
115+
:param client: InfluxDBClient instance for writing points to InfluxDB.
116+
:attention: if a client is provided, it will supersede the one defined in the Helper.
95117
:return result of client.write_points.
96118
'''
97-
rtn = cls._client.write_points(cls._json_body_())
119+
if not client:
120+
client = cls._client
121+
rtn = client.write_points(cls._json_body_())
98122
cls._reset_()
99123
return rtn
100124

tests/influxdb/helper_test.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# -*- coding: utf-8 -*-
22

33
import unittest
4+
import warnings
45

56
from influxdb import SeriesHelper, InfluxDBClient
7+
from requests.exceptions import ConnectionError
68

79

810
class TestSeriesHelper(unittest.TestCase):
@@ -18,7 +20,7 @@ class Meta:
1820
series_name = 'events.stats.{server_name}'
1921
fields = ['time', 'server_name']
2022
bulk_size = 5
21-
autocommit = False
23+
autocommit = True
2224

2325
TestSeriesHelper.MySeriesHelper = MySeriesHelper
2426

@@ -36,6 +38,7 @@ def testSingleSeriesName(self):
3638
[156, 'us.east-1']],
3739
'name': 'events.stats.us.east-1',
3840
'columns': ['time', 'server_name']}]
41+
3942
rcvd = TestSeriesHelper.MySeriesHelper._json_body_()
4043
self.assertTrue(all([el in expectation for el in rcvd]) and all([el in rcvd for el in expectation]), 'Invalid JSON body of time series returned from _json_body_ for one series name: {}.'.format(rcvd))
4144
TestSeriesHelper.MySeriesHelper._reset_()
@@ -61,6 +64,7 @@ def testSeveralSeriesNames(self):
6164
{'points': [[159, 'us.east-1']],
6265
'name': 'events.stats.us.east-1',
6366
'columns': ['time', 'server_name']}]
67+
6468
rcvd = TestSeriesHelper.MySeriesHelper._json_body_()
6569
self.assertTrue(all([el in expectation for el in rcvd]) and all([el in rcvd for el in expectation]), 'Invalid JSON body of time series returned from _json_body_ for several series names: {}.'.format(rcvd))
6670
TestSeriesHelper.MySeriesHelper._reset_()
@@ -77,16 +81,43 @@ class MissingClient(SeriesHelper):
7781
class Meta:
7882
series_name = 'events.stats.{server_name}'
7983
fields = ['time', 'server_name']
84+
autocommit = True
8085

8186
class MissingSeriesName(SeriesHelper):
8287
class Meta:
83-
client = TestSeriesHelper.client
8488
fields = ['time', 'server_name']
85-
89+
8690
class MissingFields(SeriesHelper):
8791
class Meta:
88-
client = TestSeriesHelper.client
8992
series_name = 'events.stats.{server_name}'
90-
93+
9194
for cls in [MissingMeta, MissingClient, MissingFields, MissingSeriesName]:
9295
self.assertRaises(AttributeError, cls, **{'time': 159, 'server_name': 'us.east-1'})
96+
97+
def testWarnings(self):
98+
'''
99+
Tests warnings for code coverage test.
100+
'''
101+
class WarnBulkSizeZero(SeriesHelper):
102+
class Meta:
103+
client = TestSeriesHelper.client
104+
series_name = 'events.stats.{server_name}'
105+
fields = ['time', 'server_name']
106+
bulk_size = 0
107+
autocommit = True
108+
109+
class WarnBulkSizeNoEffect(SeriesHelper):
110+
class Meta:
111+
series_name = 'events.stats.{server_name}'
112+
fields = ['time', 'server_name']
113+
bulk_size = 5
114+
autocommit = False
115+
116+
for cls in [WarnBulkSizeNoEffect, WarnBulkSizeZero]:
117+
with warnings.catch_warnings(record=True) as w:
118+
warnings.simplefilter("always")
119+
try:
120+
cls(time=159, server_name='us.east-1')
121+
except ConnectionError:
122+
pass # Server defined in the client is invalid, we're testing the warning only.
123+
self.assertEqual(len(w), 1, 'Calling {} did not generate exactly one warning.'.format(cls))

0 commit comments

Comments
 (0)