influxdb3-python 0.9.0__tar.gz → 0.11.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. influxdb3_python-0.11.0/Examples/batching_example.py +141 -0
  2. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/PKG-INFO +19 -7
  3. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/README.md +6 -5
  4. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb3_python.egg-info/PKG-INFO +19 -7
  5. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/__init__.py +3 -2
  6. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/query/query_api.py +5 -1
  7. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/version.py +1 -1
  8. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/write_api.py +2 -1
  9. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/setup.py +1 -0
  10. influxdb3_python-0.11.0/tests/test_influxdb_client_3_integration.py +186 -0
  11. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/tests/test_polars.py +29 -1
  12. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/tests/test_query.py +14 -0
  13. influxdb3_python-0.9.0/Examples/batching_example.py +0 -114
  14. influxdb3_python-0.9.0/tests/test_influxdb_client_3_integration.py +0 -74
  15. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/Examples/__init__.py +0 -0
  16. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/Examples/cloud_dedicated_query.py +0 -0
  17. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/Examples/cloud_dedicated_write.py +0 -0
  18. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/Examples/config.py +0 -0
  19. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/Examples/flight_options_example.py +0 -0
  20. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/Examples/handle_http_error.py +0 -0
  21. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/Examples/pandas_write.py +0 -0
  22. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/Examples/query_type.py +0 -0
  23. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/LICENSE +0 -0
  24. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb3_python.egg-info/SOURCES.txt +0 -0
  25. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb3_python.egg-info/dependency_links.txt +0 -0
  26. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb3_python.egg-info/requires.txt +0 -0
  27. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb3_python.egg-info/top_level.txt +0 -0
  28. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/py.typed +0 -0
  29. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/query/__init__.py +0 -0
  30. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/read_file.py +0 -0
  31. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/__init__.py +0 -0
  32. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/_sync/__init__.py +0 -0
  33. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/_sync/api_client.py +0 -0
  34. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/_sync/rest.py +0 -0
  35. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/__init__.py +0 -0
  36. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/_base.py +0 -0
  37. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/exceptions.py +0 -0
  38. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/influxdb_client.py +0 -0
  39. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/logging_handler.py +0 -0
  40. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/util/__init__.py +0 -0
  41. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/util/date_utils.py +0 -0
  42. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/util/date_utils_pandas.py +0 -0
  43. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/util/helpers.py +0 -0
  44. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/util/multiprocessing_helper.py +0 -0
  45. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/warnings.py +0 -0
  46. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/write/__init__.py +0 -0
  47. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/write/dataframe_serializer.py +0 -0
  48. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/write/point.py +0 -0
  49. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/write/polars_dataframe_serializer.py +0 -0
  50. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/client/write/retry.py +0 -0
  51. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/configuration.py +0 -0
  52. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/domain/__init__.py +0 -0
  53. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/domain/write_precision.py +0 -0
  54. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/extras.py +0 -0
  55. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/rest.py +0 -0
  56. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/service/__init__.py +0 -0
  57. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/service/_base_service.py +0 -0
  58. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/service/signin_service.py +0 -0
  59. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/service/signout_service.py +0 -0
  60. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/influxdb_client_3/write_client/service/write_service.py +0 -0
  61. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/setup.cfg +0 -0
  62. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/tests/test_api_client.py +0 -0
  63. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/tests/test_dataframe_serializer.py +0 -0
  64. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/tests/test_date_helper.py +0 -0
  65. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/tests/test_deep_merge.py +0 -0
  66. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/tests/test_influxdb_client_3.py +0 -0
  67. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/tests/test_merge_options.py +0 -0
  68. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/tests/test_point.py +0 -0
  69. {influxdb3_python-0.9.0 → influxdb3_python-0.11.0}/tests/test_write_file.py +0 -0
@@ -0,0 +1,141 @@
1
+ import datetime
2
+ import random
3
+ import time
4
+
5
+ from bson import ObjectId
6
+
7
+ import influxdb_client_3 as InfluxDBClient3
8
+ from influxdb_client_3 import write_client_options, WritePrecision, WriteOptions, InfluxDBError
9
+
10
+ from config import Config
11
+
12
+
13
+ class BatchingCallback(object):
14
+
15
+ def __init__(self):
16
+ self.write_status_msg = None
17
+ self.write_count = 0
18
+ self.retry_count = 0
19
+ self.start = time.time_ns()
20
+
21
+ def success(self, conf, data: str):
22
+ print(f"Written batch: {conf}, data: {data}")
23
+ self.write_count += 1
24
+ self.write_status_msg = f"SUCCESS: {self.write_count} writes"
25
+
26
+ def error(self, conf, data: str, exception: InfluxDBError):
27
+ print(f"Cannot write batch: {conf}, data: {data} due: {exception}")
28
+ self.write_status_msg = f"FAILURE - cause: {exception}"
29
+
30
+ def retry(self, conf, data: str, exception: InfluxDBError):
31
+ print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}")
32
+ self.retry_count += 1
33
+
34
+ def elapsed(self) -> int:
35
+ return time.time_ns() - self.start
36
+
37
+
38
+ def main() -> None:
39
+ conf = Config()
40
+
41
+ # Creating 5.000 gatewayId values as MongoDB ObjectIDs
42
+ gatewayIds = [ObjectId() for x in range(0, 100)]
43
+
44
+ # Setting decimal precision to 2
45
+ precision = 2
46
+
47
+ # Setting timestamp for first sensor reading
48
+ sample_window_days = 7
49
+ now = datetime.datetime.now()
50
+ now = now - datetime.timedelta(days=sample_window_days)
51
+ target_sample_count = sample_window_days * 24 * 60 * 6
52
+
53
+ callback = BatchingCallback()
54
+
55
+ write_options = WriteOptions(batch_size=5_000,
56
+ flush_interval=10_000,
57
+ jitter_interval=2_000,
58
+ retry_interval=5_000,
59
+ max_retries=5,
60
+ max_retry_delay=30_000,
61
+ max_close_wait=600_000,
62
+ exponential_base=2)
63
+
64
+ wco = write_client_options(success_callback=callback.success,
65
+ error_callback=callback.error,
66
+ retry_callback=callback.retry,
67
+ write_options=write_options)
68
+
69
+ # Opening InfluxDB client with a batch size of 5k points or flush interval
70
+ # of 10k ms and gzip compression
71
+ with InfluxDBClient3.InfluxDBClient3(token=conf.token,
72
+ host=conf.host,
73
+ org=conf.org,
74
+ database=conf.database,
75
+ enable_gzip=True,
76
+ write_client_options=wco) as _client:
77
+ # Creating iterator for one hour worth of data (6 sensor readings per
78
+ # minute)
79
+ print(f"Writing {target_sample_count} data points.")
80
+ for i in range(0, target_sample_count):
81
+ # Adding 10 seconds to timestamp of previous sensor reading
82
+ now = now + datetime.timedelta(seconds=10)
83
+ # Iterating over gateways
84
+ for gatewayId in gatewayIds:
85
+ # Creating random test data for 12 fields to be stored in
86
+ # timeseries database
87
+ bcW = random.randrange(1501)
88
+ bcWh = round(random.uniform(0, 4.17), precision)
89
+ bdW = random.randrange(71)
90
+ bdWh = round(random.uniform(0, 0.12), precision)
91
+ cPvWh = round(random.uniform(0.51, 27.78), precision)
92
+ cW = random.randrange(172, 10001)
93
+ cWh = round(random.uniform(0.51, 27.78), precision)
94
+ eWh = round(random.uniform(0, 41.67), precision)
95
+ iWh = round(random.uniform(0, 16.67), precision)
96
+ pW = random.randrange(209, 20001)
97
+ pWh = round(random.uniform(0.58, 55.56), precision)
98
+ scWh = round(random.uniform(0.58, 55.56), precision)
99
+ # Creating point to be ingested into InfluxDB
100
+ p = InfluxDBClient3.Point("stream").tag(
101
+ "gatewayId",
102
+ str(gatewayId)).field(
103
+ "bcW",
104
+ bcW).field(
105
+ "bcWh",
106
+ bcWh).field(
107
+ "bdW",
108
+ bdW).field(
109
+ "bdWh",
110
+ bdWh).field(
111
+ "cPvWh",
112
+ cPvWh).field(
113
+ "cW",
114
+ cW).field(
115
+ "cWh",
116
+ cWh).field(
117
+ "eWh",
118
+ eWh).field(
119
+ "iWh",
120
+ iWh).field(
121
+ "pW",
122
+ pW).field(
123
+ "pWh",
124
+ pWh).field(
125
+ "scWh",
126
+ scWh).time(
127
+ now.strftime('%Y-%m-%dT%H:%M:%SZ'),
128
+ WritePrecision.S)
129
+
130
+ # Writing point (InfluxDB automatically batches writes into sets of
131
+ # 5k points)
132
+ _client.write(record=p)
133
+
134
+ print(callback.write_status_msg)
135
+ print(f"Write retries: {callback.retry_count}")
136
+ print(f"Wrote {target_sample_count} data points.")
137
+ print(f"Elapsed time ms: {int(callback.elapsed() / 1_000_000)}")
138
+
139
+
140
+ if __name__ == "__main__":
141
+ main()
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: influxdb3-python
3
- Version: 0.9.0
3
+ Version: 0.11.0
4
4
  Summary: Community Python client for InfluxDB 3.0
5
5
  Home-page: https://github.com/InfluxCommunity/influxdb3-python
6
6
  Author: InfluxData
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
16
17
  Requires-Python: >=3.8
17
18
  Description-Content-Type: text/markdown
18
19
  License-File: LICENSE
@@ -32,6 +33,16 @@ Requires-Dist: polars; extra == "dataframe"
32
33
  Provides-Extra: test
33
34
  Requires-Dist: pytest; extra == "test"
34
35
  Requires-Dist: pytest-cov; extra == "test"
36
+ Dynamic: author
37
+ Dynamic: author-email
38
+ Dynamic: classifier
39
+ Dynamic: description
40
+ Dynamic: description-content-type
41
+ Dynamic: home-page
42
+ Dynamic: provides-extra
43
+ Dynamic: requires-dist
44
+ Dynamic: requires-python
45
+ Dynamic: summary
35
46
 
36
47
  <p align="center">
37
48
  <img src="https://github.com/InfluxCommunity/influxdb3-python/blob/main/python-logo.png?raw=true" alt="Your Image" width="150px">
@@ -127,7 +138,11 @@ from influxdb_client_3 import write_client_options, WritePrecision, WriteOptions
127
138
 
128
139
  class BatchingCallback(object):
129
140
 
141
+ def __init__(self):
142
+ self.write_count = 0
143
+
130
144
  def success(self, conf, data: str):
145
+ self.write_count += 1
131
146
  print(f"Written batch: {conf}, data: {data}")
132
147
 
133
148
  def error(self, conf, data: str, exception: InfluxDBError):
@@ -138,7 +153,7 @@ class BatchingCallback(object):
138
153
 
139
154
  callback = BatchingCallback()
140
155
 
141
- write_options = WriteOptions(batch_size=500,
156
+ write_options = WriteOptions(batch_size=100,
142
157
  flush_interval=10_000,
143
158
  jitter_interval=2_000,
144
159
  retry_interval=5_000,
@@ -163,10 +178,7 @@ with InfluxDBClient3.InfluxDBClient3(
163
178
  file='./out.csv',
164
179
  timestamp_column='time', tag_columns=["provider", "machineID"])
165
180
 
166
- client.write_file(
167
- file='./out.json',
168
- timestamp_column='time', tag_columns=["provider", "machineID"], date_unit='ns' )
169
-
181
+ print(f'DONE writing from csv in {callback.write_count} batch(es)')
170
182
 
171
183
  ```
172
184
 
@@ -92,7 +92,11 @@ from influxdb_client_3 import write_client_options, WritePrecision, WriteOptions
92
92
 
93
93
  class BatchingCallback(object):
94
94
 
95
+ def __init__(self):
96
+ self.write_count = 0
97
+
95
98
  def success(self, conf, data: str):
99
+ self.write_count += 1
96
100
  print(f"Written batch: {conf}, data: {data}")
97
101
 
98
102
  def error(self, conf, data: str, exception: InfluxDBError):
@@ -103,7 +107,7 @@ class BatchingCallback(object):
103
107
 
104
108
  callback = BatchingCallback()
105
109
 
106
- write_options = WriteOptions(batch_size=500,
110
+ write_options = WriteOptions(batch_size=100,
107
111
  flush_interval=10_000,
108
112
  jitter_interval=2_000,
109
113
  retry_interval=5_000,
@@ -128,10 +132,7 @@ with InfluxDBClient3.InfluxDBClient3(
128
132
  file='./out.csv',
129
133
  timestamp_column='time', tag_columns=["provider", "machineID"])
130
134
 
131
- client.write_file(
132
- file='./out.json',
133
- timestamp_column='time', tag_columns=["provider", "machineID"], date_unit='ns' )
134
-
135
+ print(f'DONE writing from csv in {callback.write_count} batch(es)')
135
136
 
136
137
  ```
137
138
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: influxdb3-python
3
- Version: 0.9.0
3
+ Version: 0.11.0
4
4
  Summary: Community Python client for InfluxDB 3.0
5
5
  Home-page: https://github.com/InfluxCommunity/influxdb3-python
6
6
  Author: InfluxData
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.9
13
13
  Classifier: Programming Language :: Python :: 3.10
14
14
  Classifier: Programming Language :: Python :: 3.11
15
15
  Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
16
17
  Requires-Python: >=3.8
17
18
  Description-Content-Type: text/markdown
18
19
  License-File: LICENSE
@@ -32,6 +33,16 @@ Requires-Dist: polars; extra == "dataframe"
32
33
  Provides-Extra: test
33
34
  Requires-Dist: pytest; extra == "test"
34
35
  Requires-Dist: pytest-cov; extra == "test"
36
+ Dynamic: author
37
+ Dynamic: author-email
38
+ Dynamic: classifier
39
+ Dynamic: description
40
+ Dynamic: description-content-type
41
+ Dynamic: home-page
42
+ Dynamic: provides-extra
43
+ Dynamic: requires-dist
44
+ Dynamic: requires-python
45
+ Dynamic: summary
35
46
 
36
47
  <p align="center">
37
48
  <img src="https://github.com/InfluxCommunity/influxdb3-python/blob/main/python-logo.png?raw=true" alt="Your Image" width="150px">
@@ -127,7 +138,11 @@ from influxdb_client_3 import write_client_options, WritePrecision, WriteOptions
127
138
 
128
139
  class BatchingCallback(object):
129
140
 
141
+ def __init__(self):
142
+ self.write_count = 0
143
+
130
144
  def success(self, conf, data: str):
145
+ self.write_count += 1
131
146
  print(f"Written batch: {conf}, data: {data}")
132
147
 
133
148
  def error(self, conf, data: str, exception: InfluxDBError):
@@ -138,7 +153,7 @@ class BatchingCallback(object):
138
153
 
139
154
  callback = BatchingCallback()
140
155
 
141
- write_options = WriteOptions(batch_size=500,
156
+ write_options = WriteOptions(batch_size=100,
142
157
  flush_interval=10_000,
143
158
  jitter_interval=2_000,
144
159
  retry_interval=5_000,
@@ -163,10 +178,7 @@ with InfluxDBClient3.InfluxDBClient3(
163
178
  file='./out.csv',
164
179
  timestamp_column='time', tag_columns=["provider", "machineID"])
165
180
 
166
- client.write_file(
167
- file='./out.json',
168
- timestamp_column='time', tag_columns=["provider", "machineID"], date_unit='ns' )
169
-
181
+ print(f'DONE writing from csv in {callback.write_count} batch(es)')
170
182
 
171
183
  ```
172
184
 
@@ -121,7 +121,7 @@ class InfluxDBClient3:
121
121
  possible.
122
122
  :key str proxy: Set this to configure the http proxy to be used (ex. http://localhost:3128)
123
123
  :key str proxy_headers: A dictionary containing headers that will be sent to the proxy. Could be used for proxy
124
- authentication.
124
+ authentication. (Applies to Write API only)
125
125
  :key int connection_pool_maxsize: Number of connections to save that can be reused by urllib3.
126
126
  Defaults to "multiprocessing.cpu_count() * 5".
127
127
  :key urllib3.util.retry.Retry retries: Set the default retry strategy that is used for all HTTP requests
@@ -166,7 +166,8 @@ class InfluxDBClient3:
166
166
  else:
167
167
  connection_string = f"grpc+tcp://{hostname}:{port}"
168
168
  self._query_api = _QueryApi(connection_string=connection_string, token=token,
169
- flight_client_options=flight_client_options)
169
+ flight_client_options=flight_client_options,
170
+ proxy=kwargs.get("proxy", None))
170
171
 
171
172
  def write(self, record=None, database=None, **kwargs):
172
173
  """
@@ -25,7 +25,8 @@ class QueryApi(object):
25
25
  def __init__(self,
26
26
  connection_string,
27
27
  token,
28
- flight_client_options) -> None:
28
+ flight_client_options,
29
+ proxy=None) -> None:
29
30
  """
30
31
  Initialize defaults.
31
32
 
@@ -35,9 +36,12 @@ class QueryApi(object):
35
36
  """
36
37
  self._token = token
37
38
  self._flight_client_options = flight_client_options or {}
39
+ self._proxy = proxy
38
40
  self._flight_client_options["generic_options"] = [
39
41
  ("grpc.secondary_user_agent", USER_AGENT)
40
42
  ]
43
+ if self._proxy:
44
+ self._flight_client_options["generic_options"].append(("grpc.http_proxy", self._proxy))
41
45
  self._flight_client = FlightClient(connection_string, **self._flight_client_options)
42
46
 
43
47
  def query(self, query: str, language: str, mode: str, database: str, **kwargs):
@@ -1,4 +1,4 @@
1
1
  """Version of the Client that is used in User-Agent header."""
2
2
 
3
- VERSION = '0.9.0'
3
+ VERSION = '0.11.0'
4
4
  USER_AGENT = f'influxdb3-python/{VERSION}'
@@ -461,7 +461,8 @@ You can use native asynchronous version of the client:
461
461
  precision, **kwargs)
462
462
 
463
463
  elif 'polars' in str(type(data)):
464
- from influxdb_client_3.write_client.client.write.dataframe_serializer import PolarsDataframeSerializer
464
+ from influxdb_client_3.write_client.client.write.polars_dataframe_serializer \
465
+ import PolarsDataframeSerializer
465
466
  serializer = PolarsDataframeSerializer(data,
466
467
  self._point_settings, precision,
467
468
  self._write_options.batch_size, **kwargs)
@@ -65,5 +65,6 @@ setup(
65
65
  'Programming Language :: Python :: 3.10',
66
66
  'Programming Language :: Python :: 3.11',
67
67
  'Programming Language :: Python :: 3.12',
68
+ 'Programming Language :: Python :: 3.13',
68
69
  ]
69
70
  )
@@ -0,0 +1,186 @@
1
+ import logging
2
+ import os
3
+ import random
4
+ import string
5
+ import time
6
+ import unittest
7
+
8
+ import pyarrow
9
+ import pytest
10
+
11
+ from influxdb_client_3 import InfluxDBClient3, InfluxDBError, write_client_options, WriteOptions
12
+
13
+
14
+ def random_hex(len=6):
15
+ return ''.join(random.choice(string.hexdigits) for i in range(len))
16
+
17
+
18
+ @pytest.mark.integration
19
+ @pytest.mark.skipif(
20
+ not all(
21
+ [
22
+ os.getenv('TESTING_INFLUXDB_URL'),
23
+ os.getenv('TESTING_INFLUXDB_TOKEN'),
24
+ os.getenv('TESTING_INFLUXDB_DATABASE'),
25
+ ]
26
+ ),
27
+ reason="Integration test environment variables not set.",
28
+ )
29
+ class TestInfluxDBClient3Integration(unittest.TestCase):
30
+
31
+ @pytest.fixture(autouse=True)
32
+ def inject_fixtures(self, caplog):
33
+ self._caplog = caplog
34
+
35
+ def setUp(self):
36
+ self.host = os.getenv('TESTING_INFLUXDB_URL')
37
+ self.token = os.getenv('TESTING_INFLUXDB_TOKEN')
38
+ self.database = os.getenv('TESTING_INFLUXDB_DATABASE')
39
+ self.client = InfluxDBClient3(host=self.host, database=self.database, token=self.token)
40
+
41
+ def tearDown(self):
42
+ self._caplog.clear()
43
+ self._caplog.set_level(logging.ERROR)
44
+ if self.client:
45
+ self.client.close()
46
+
47
+ def test_write_and_query(self):
48
+ test_id = time.time_ns()
49
+ self.client.write(f"integration_test_python,type=used value=123.0,test_id={test_id}i")
50
+
51
+ sql = 'SELECT * FROM integration_test_python where type=$type and test_id=$test_id'
52
+
53
+ df = self.client.query(sql, mode="pandas", query_parameters={'type': 'used', 'test_id': test_id})
54
+
55
+ self.assertIsNotNone(df)
56
+ self.assertEqual(1, len(df))
57
+ self.assertEqual(test_id, df['test_id'][0])
58
+ self.assertEqual(123.0, df['value'][0])
59
+
60
+ def test_auth_error_token(self):
61
+ self.client = InfluxDBClient3(host=self.host, database=self.database, token='fake token')
62
+ test_id = time.time_ns()
63
+ with self.assertRaises(InfluxDBError) as err:
64
+ self.client.write(f"integration_test_python,type=used value=123.0,test_id={test_id}i")
65
+ self.assertEqual('unauthorized access', err.exception.message) # Cloud
66
+
67
+ def test_auth_error_auth_scheme(self):
68
+ self.client = InfluxDBClient3(host=self.host, database=self.database, token=self.token, auth_scheme='Any')
69
+ test_id = time.time_ns()
70
+ with self.assertRaises(InfluxDBError) as err:
71
+ self.client.write(f"integration_test_python,type=used value=123.0,test_id={test_id}i")
72
+ self.assertEqual('unauthorized access', err.exception.message) # Cloud
73
+
74
+ def test_error_headers(self):
75
+ self.client = InfluxDBClient3(host=self.host, database=self.database, token=self.token)
76
+ with self.assertRaises(InfluxDBError) as err:
77
+ self.client.write("integration_test_python,type=used value=123.0,test_id=")
78
+ self.assertIn("Could not parse entire line. Found trailing content:", err.exception.message)
79
+ headers = err.exception.getheaders()
80
+ try:
81
+ self.assertIsNotNone(headers)
82
+ self.assertRegex(headers['trace-id'], '[0-9a-f]{16}')
83
+ self.assertEqual('false', headers['trace-sampled'])
84
+ self.assertIsNotNone(headers['Strict-Transport-Security'])
85
+ self.assertRegex(headers['X-Influxdb-Request-ID'], '[0-9a-f]+')
86
+ self.assertIsNotNone(headers['X-Influxdb-Build'])
87
+ except KeyError as ke:
88
+ self.fail(f'Header {ke} not found')
89
+
90
+ def test_batch_write_callbacks(self):
91
+ write_success = False
92
+ write_error = False
93
+ write_count = 0
94
+
95
+ measurement = f'test{random_hex(6)}'
96
+ data_set_size = 40
97
+ batch_size = 10
98
+
99
+ def success(conf, data):
100
+ nonlocal write_success, write_count
101
+ write_success = True
102
+ write_count += 1
103
+
104
+ def error(conf, data, exception: InfluxDBError):
105
+ nonlocal write_error
106
+ write_error = True
107
+
108
+ w_opts = WriteOptions(
109
+ batch_size=batch_size,
110
+ flush_interval=1_000,
111
+ jitter_interval=500
112
+ )
113
+
114
+ wc_opts = write_client_options(
115
+ success_callback=success,
116
+ error_callback=error,
117
+ write_options=w_opts
118
+ )
119
+
120
+ now = time.time_ns()
121
+ with InfluxDBClient3(host=self.host,
122
+ database=self.database,
123
+ token=self.token,
124
+ write_client_options=wc_opts) as w_client:
125
+
126
+ for i in range(0, data_set_size):
127
+ w_client.write(f'{measurement},location=harfa val={i}i {now - (i * 1_000_000_000)}')
128
+
129
+ self.assertEqual(data_set_size / batch_size, write_count)
130
+ self.assertTrue(write_success)
131
+ self.assertFalse(write_error)
132
+
133
+ with InfluxDBClient3(host=self.host,
134
+ database=self.database,
135
+ token=self.token,
136
+ write_client_options=wc_opts) as r_client:
137
+
138
+ query = f"SELECT * FROM \"{measurement}\" WHERE time >= now() - interval '3 minute'"
139
+ reader: pyarrow.Table = r_client.query(query)
140
+ list_results = reader.to_pylist()
141
+ self.assertEqual(data_set_size, len(list_results))
142
+
143
+ def test_batch_write_closed(self):
144
+
145
+ self._caplog.set_level(logging.DEBUG)
146
+ # writing measurement for last cca 3hrs
147
+ # so repeat runs in that time frame could
148
+ # result in clashed result data if always
149
+ # using same measurement name
150
+ measurement = f'test{random_hex()}'
151
+ data_size = 10_000
152
+ w_opts = WriteOptions(
153
+ batch_size=100,
154
+ flush_interval=3_000,
155
+ jitter_interval=500
156
+ )
157
+
158
+ wc_opts = write_client_options(
159
+ write_options=w_opts
160
+ )
161
+
162
+ now = time.time_ns()
163
+ with InfluxDBClient3(host=self.host,
164
+ database=self.database,
165
+ token=self.token,
166
+ write_client_options=wc_opts,
167
+ debug=True) as w_client:
168
+
169
+ for i in range(0, data_size):
170
+ w_client.write(f'{measurement},location=harfa val={i}i {now - (i * 1_000_000_000)}')
171
+
172
+ lines = self._caplog.text.splitlines()
173
+ self.assertRegex(lines[len(lines) - 1], ".*the batching processor was disposed$")
174
+
175
+ logging.info("WRITING DONE")
176
+ with InfluxDBClient3(host=self.host,
177
+ database=self.database,
178
+ token=self.token,
179
+ write_client_options=wc_opts) as r_client:
180
+
181
+ logging.info("PREPARING QUERY")
182
+
183
+ query = f"SELECT * FROM \"{measurement}\" WHERE time >= now() - interval '3 hours'"
184
+ reader: pyarrow.Table = r_client.query(query, mode="")
185
+ list_results = reader.to_pylist()
186
+ self.assertEqual(data_size, len(list_results))
@@ -2,7 +2,7 @@ import importlib.util
2
2
  import unittest
3
3
  from unittest.mock import Mock
4
4
 
5
- from influxdb_client_3 import PointSettings, InfluxDBClient3
5
+ from influxdb_client_3 import PointSettings, InfluxDBClient3, write_client_options, WriteOptions
6
6
  from influxdb_client_3.write_client import WriteService
7
7
  from influxdb_client_3.write_client.client.write.polars_dataframe_serializer import polars_data_frame_to_list_of_points
8
8
 
@@ -61,3 +61,31 @@ class TestWritePolars(unittest.TestCase):
61
61
  actual = self.client._write_api._write_service.post_write.call_args[1]['body']
62
62
  self.assertEqual(b'measurement temperature=22.4 1722470400000000000\n'
63
63
  b'measurement temperature=21.8 1722474000000000000', actual)
64
+
65
+ def test_write_polars_batching(self):
66
+ import polars as pl
67
+ df = pl.DataFrame({
68
+ "time": pl.Series(["2024-08-01 00:00:00", "2024-08-01 01:00:00"]).str.to_datetime(time_unit='ns'),
69
+ "temperature": [22.4, 21.8],
70
+ })
71
+ self.client = InfluxDBClient3(
72
+ host="localhost",
73
+ org="my_org",
74
+ database="my_db",
75
+ token="my_token", write_client_options=write_client_options(
76
+ write_options=WriteOptions(batch_size=2)
77
+ )
78
+ )
79
+ self.client._write_api._write_options = WriteOptions(batch_size=2)
80
+ self.client._write_api._write_service = Mock(spec=WriteService)
81
+
82
+ self.client.write(
83
+ database="database",
84
+ record=df,
85
+ data_frame_measurement_name="measurement",
86
+ data_frame_timestamp_column="time",
87
+ )
88
+
89
+ actual = self.client._write_api._write_service.post_write.call_args[1]['body']
90
+ self.assertEqual(b'measurement temperature=22.4 1722470400000000000\n'
91
+ b'measurement temperature=21.8 1722474000000000000', actual)
@@ -150,3 +150,17 @@ class TestQuery(unittest.TestCase):
150
150
  )
151
151
 
152
152
  mock_do_get.assert_called_once_with(expected_ticket, ANY)
153
+
154
+ def test_query_proxy_base_client(self):
155
+ test_proxy = "http://testproxy:5432"
156
+ client = InfluxDBClient3(
157
+ host="http://localhost:8443",
158
+ token="my-token",
159
+ org="my-org",
160
+ database="my-database",
161
+ proxy=test_proxy
162
+ )
163
+
164
+ assert client._query_api._proxy == test_proxy
165
+ assert ('grpc.http_proxy', test_proxy) in\
166
+ client._query_api._flight_client_options.get('generic_options')
@@ -1,114 +0,0 @@
1
- import datetime
2
- import random
3
-
4
- from bson import ObjectId
5
-
6
- import influxdb_client_3 as InfluxDBClient3
7
- from influxdb_client_3 import write_client_options, WritePrecision, WriteOptions, InfluxDBError
8
-
9
-
10
- class BatchingCallback(object):
11
-
12
- def success(self, conf, data: str):
13
- print(f"Written batch: {conf}, data: {data}")
14
-
15
- def error(self, conf, data: str, exception: InfluxDBError):
16
- print(f"Cannot write batch: {conf}, data: {data} due: {exception}")
17
-
18
- def retry(self, conf, data: str, exception: InfluxDBError):
19
- print(f"Retryable error occurs for batch: {conf}, data: {data} retry: {exception}")
20
-
21
-
22
- # Creating 5.000 gatewayId values as MongoDB ObjectIDs
23
- gatewayIds = [ObjectId() for x in range(0, 100)]
24
-
25
- # Setting decimal precision to 2
26
- precision = 2
27
-
28
- # Setting timestamp for first sensor reading
29
- now = datetime.datetime.now()
30
- now = now - datetime.timedelta(days=30)
31
- teststart = datetime.datetime.now()
32
-
33
- # InfluxDB connection details
34
- token = ""
35
- org = ""
36
- database = ""
37
- url = "eu-central-1-1.aws.cloud2.influxdata.com"
38
-
39
- callback = BatchingCallback()
40
-
41
- write_options = WriteOptions(batch_size=5_000,
42
- flush_interval=10_000,
43
- jitter_interval=2_000,
44
- retry_interval=5_000,
45
- max_retries=5,
46
- max_retry_delay=30_000,
47
- exponential_base=2)
48
-
49
- wco = write_client_options(success_callback=callback.success,
50
- error_callback=callback.error,
51
- retry_callback=callback.retry,
52
- write_options=write_options
53
- )
54
- # Opening InfluxDB client with a batch size of 5k points or flush interval
55
- # of 10k ms and gzip compression
56
- with InfluxDBClient3.InfluxDBClient3(token=token,
57
- host=url,
58
- org=org,
59
- database=database, enable_gzip=True, write_client_options=wco) as _client:
60
- # Creating iterator for one hour worth of data (6 sensor readings per
61
- # minute)
62
- for i in range(0, 525600):
63
- # Adding 10 seconds to timestamp of previous sensor reading
64
- now = now + datetime.timedelta(seconds=10)
65
- # Iterating over gateways
66
- for gatewayId in gatewayIds:
67
- # Creating random test data for 12 fields to be stored in
68
- # timeseries database
69
- bcW = random.randrange(1501)
70
- bcWh = round(random.uniform(0, 4.17), precision)
71
- bdW = random.randrange(71)
72
- bdWh = round(random.uniform(0, 0.12), precision)
73
- cPvWh = round(random.uniform(0.51, 27.78), precision)
74
- cW = random.randrange(172, 10001)
75
- cWh = round(random.uniform(0.51, 27.78), precision)
76
- eWh = round(random.uniform(0, 41.67), precision)
77
- iWh = round(random.uniform(0, 16.67), precision)
78
- pW = random.randrange(209, 20001)
79
- pWh = round(random.uniform(0.58, 55.56), precision)
80
- scWh = round(random.uniform(0.58, 55.56), precision)
81
- # Creating point to be ingested into InfluxDB
82
- p = InfluxDBClient3.Point("stream").tag(
83
- "gatewayId",
84
- str(gatewayId)).field(
85
- "bcW",
86
- bcW).field(
87
- "bcWh",
88
- bcWh).field(
89
- "bdW",
90
- bdW).field(
91
- "bdWh",
92
- bdWh).field(
93
- "cPvWh",
94
- cPvWh).field(
95
- "cW",
96
- cW).field(
97
- "cWh",
98
- cWh).field(
99
- "eWh",
100
- eWh).field(
101
- "iWh",
102
- iWh).field(
103
- "pW",
104
- pW).field(
105
- "pWh",
106
- pWh).field(
107
- "scWh",
108
- scWh).time(
109
- now.strftime('%Y-%m-%dT%H:%M:%SZ'),
110
- WritePrecision.S)
111
-
112
- # Writing point (InfluxDB automatically batches writes into sets of
113
- # 5k points)
114
- _client.write(record=p)
@@ -1,74 +0,0 @@
1
- import os
2
- import time
3
- import unittest
4
-
5
- import pytest
6
-
7
- from influxdb_client_3 import InfluxDBClient3, InfluxDBError
8
-
9
-
10
- @pytest.mark.integration
11
- @pytest.mark.skipif(
12
- not all(
13
- [
14
- os.getenv('TESTING_INFLUXDB_URL'),
15
- os.getenv('TESTING_INFLUXDB_TOKEN'),
16
- os.getenv('TESTING_INFLUXDB_DATABASE'),
17
- ]
18
- ),
19
- reason="Integration test environment variables not set.",
20
- )
21
- class TestInfluxDBClient3Integration(unittest.TestCase):
22
-
23
- def setUp(self):
24
- self.host = os.getenv('TESTING_INFLUXDB_URL')
25
- self.token = os.getenv('TESTING_INFLUXDB_TOKEN')
26
- self.database = os.getenv('TESTING_INFLUXDB_DATABASE')
27
- self.client = InfluxDBClient3(host=self.host, database=self.database, token=self.token)
28
-
29
- def tearDown(self):
30
- if self.client:
31
- self.client.close()
32
-
33
- def test_write_and_query(self):
34
- test_id = time.time_ns()
35
- self.client.write(f"integration_test_python,type=used value=123.0,test_id={test_id}i")
36
-
37
- sql = 'SELECT * FROM integration_test_python where type=$type and test_id=$test_id'
38
-
39
- df = self.client.query(sql, mode="pandas", query_parameters={'type': 'used', 'test_id': test_id})
40
-
41
- self.assertIsNotNone(df)
42
- self.assertEqual(1, len(df))
43
- self.assertEqual(test_id, df['test_id'][0])
44
- self.assertEqual(123.0, df['value'][0])
45
-
46
- def test_auth_error_token(self):
47
- self.client = InfluxDBClient3(host=self.host, database=self.database, token='fake token')
48
- test_id = time.time_ns()
49
- with self.assertRaises(InfluxDBError) as err:
50
- self.client.write(f"integration_test_python,type=used value=123.0,test_id={test_id}i")
51
- self.assertEqual('unauthorized access', err.exception.message) # Cloud
52
-
53
- def test_auth_error_auth_scheme(self):
54
- self.client = InfluxDBClient3(host=self.host, database=self.database, token=self.token, auth_scheme='Any')
55
- test_id = time.time_ns()
56
- with self.assertRaises(InfluxDBError) as err:
57
- self.client.write(f"integration_test_python,type=used value=123.0,test_id={test_id}i")
58
- self.assertEqual('unauthorized access', err.exception.message) # Cloud
59
-
60
- def test_error_headers(self):
61
- self.client = InfluxDBClient3(host=self.host, database=self.database, token=self.token)
62
- with self.assertRaises(InfluxDBError) as err:
63
- self.client.write("integration_test_python,type=used value=123.0,test_id=")
64
- self.assertIn("Could not parse entire line. Found trailing content:", err.exception.message)
65
- headers = err.exception.getheaders()
66
- try:
67
- self.assertIsNotNone(headers)
68
- self.assertRegex(headers['trace-id'], '[0-9a-f]{16}')
69
- self.assertEqual('false', headers['trace-sampled'])
70
- self.assertIsNotNone(headers['Strict-Transport-Security'])
71
- self.assertRegex(headers['X-Influxdb-Request-ID'], '[0-9a-f]+')
72
- self.assertIsNotNone(headers['X-Influxdb-Build'])
73
- except KeyError as ke:
74
- self.fail(f'Header {ke} not found')