influxdb3-python 0.4.0__tar.gz → 0.6.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.
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/PKG-INFO +11 -7
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/README.md +7 -6
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb3_python.egg-info/PKG-INFO +11 -7
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb3_python.egg-info/SOURCES.txt +10 -2
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb3_python.egg-info/requires.txt +4 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/__init__.py +43 -57
- influxdb3_python-0.6.0/influxdb_client_3/py.typed +0 -0
- {influxdb3_python-0.4.0/influxdb_client_3/write_client → influxdb3_python-0.6.0/influxdb_client_3}/version.py +2 -1
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/__init__.py +1 -1
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/_sync/api_client.py +2 -2
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/util/date_utils.py +5 -1
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/write/point.py +1 -1
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/setup.py +9 -2
- influxdb3_python-0.6.0/tests/test_api_client.py +71 -0
- influxdb3_python-0.6.0/tests/test_date_helper.py +21 -0
- influxdb3_python-0.6.0/tests/test_deep_merge.py +54 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/tests/test_influxdb_client_3.py +4 -4
- influxdb3_python-0.6.0/tests/test_influxdb_client_3_integration.py +45 -0
- influxdb3_python-0.6.0/tests/test_merge_options.py +28 -0
- influxdb3_python-0.6.0/tests/test_point.py +14 -0
- influxdb3_python-0.6.0/tests/test_query.py +152 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/LICENSE +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb3_python.egg-info/dependency_links.txt +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb3_python.egg-info/top_level.txt +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/read_file.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/_sync/__init__.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/_sync/rest.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/__init__.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/_base.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/exceptions.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/influxdb_client.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/logging_handler.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/util/__init__.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/util/date_utils_pandas.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/util/helpers.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/util/multiprocessing_helper.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/warnings.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/write/__init__.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/write/dataframe_serializer.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/write/retry.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/write_api.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/configuration.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/domain/__init__.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/domain/write_precision.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/extras.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/rest.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/service/__init__.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/service/_base_service.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/service/signin_service.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/service/signout_service.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/service/write_service.py +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/setup.cfg +0 -0
- {influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/tests/test_dataframe_serializer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: influxdb3-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -29,6 +29,9 @@ Requires-Dist: polars; extra == "polars"
|
|
|
29
29
|
Provides-Extra: dataframe
|
|
30
30
|
Requires-Dist: pandas; extra == "dataframe"
|
|
31
31
|
Requires-Dist: polars; extra == "dataframe"
|
|
32
|
+
Provides-Extra: test
|
|
33
|
+
Requires-Dist: pytest; extra == "test"
|
|
34
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
32
35
|
|
|
33
36
|
<p align="center">
|
|
34
37
|
<img src="https://github.com/InfluxCommunity/influxdb3-python/blob/main/python-logo.png?raw=true" alt="Your Image" width="150px">
|
|
@@ -60,11 +63,12 @@ Requires-Dist: polars; extra == "dataframe"
|
|
|
60
63
|
|
|
61
64
|
`influxdb_client_3` is a Python module that provides a simple and convenient way to interact with InfluxDB 3.0. This module supports both writing data to InfluxDB and querying data using the Flight client, which allows you to execute SQL and InfluxQL queries on InfluxDB 3.0.
|
|
62
65
|
|
|
66
|
+
We offer a ["Getting Started: InfluxDB 3.0 Python Client Library"](https://www.youtube.com/watch?v=tpdONTm1GC8) video that goes over how to use the library and goes over the examples.
|
|
63
67
|
## Dependencies
|
|
64
68
|
|
|
65
69
|
- `pyarrow` (automatically installed)
|
|
66
70
|
- `pandas` (optional)
|
|
67
|
-
|
|
71
|
+
|
|
68
72
|
|
|
69
73
|
## Installation
|
|
70
74
|
|
|
@@ -145,7 +149,7 @@ write_options = WriteOptions(batch_size=500,
|
|
|
145
149
|
wco = write_client_options(success_callback=callback.success,
|
|
146
150
|
error_callback=callback.error,
|
|
147
151
|
retry_callback=callback.retry,
|
|
148
|
-
|
|
152
|
+
write_options=write_options
|
|
149
153
|
)
|
|
150
154
|
|
|
151
155
|
with InfluxDBClient3.InfluxDBClient3(
|
|
@@ -158,11 +162,11 @@ with InfluxDBClient3.InfluxDBClient3(
|
|
|
158
162
|
client.write_file(
|
|
159
163
|
file='./out.csv',
|
|
160
164
|
timestamp_column='time', tag_columns=["provider", "machineID"])
|
|
161
|
-
|
|
165
|
+
|
|
162
166
|
client.write_file(
|
|
163
167
|
file='./out.json',
|
|
164
168
|
timestamp_column='time', tag_columns=["provider", "machineID"], date_unit='ns' )
|
|
165
|
-
|
|
169
|
+
|
|
166
170
|
|
|
167
171
|
```
|
|
168
172
|
|
|
@@ -176,7 +180,7 @@ client._write_api.write(bucket="pokemon-codex", record=pd_df, data_frame_measure
|
|
|
176
180
|
client._write_api.write(bucket="pokemon-codex", record=pl_df, data_frame_measurement_name='caught', data_frame_tag_columns=['trainer', 'id', 'num'], data_frame_timestamp_column='timestamp')
|
|
177
181
|
```
|
|
178
182
|
|
|
179
|
-
## Querying
|
|
183
|
+
## Querying
|
|
180
184
|
|
|
181
185
|
### Querying with SQL
|
|
182
186
|
```python
|
|
@@ -228,4 +232,4 @@ table = client.query(
|
|
|
228
232
|
|
|
229
233
|
print(table.to_pandas())
|
|
230
234
|
```
|
|
231
|
-
You may also include your own root certificate via this manor aswell.
|
|
235
|
+
You may also include your own root certificate via this manor aswell.
|
|
@@ -28,11 +28,12 @@
|
|
|
28
28
|
|
|
29
29
|
`influxdb_client_3` is a Python module that provides a simple and convenient way to interact with InfluxDB 3.0. This module supports both writing data to InfluxDB and querying data using the Flight client, which allows you to execute SQL and InfluxQL queries on InfluxDB 3.0.
|
|
30
30
|
|
|
31
|
+
We offer a ["Getting Started: InfluxDB 3.0 Python Client Library"](https://www.youtube.com/watch?v=tpdONTm1GC8) video that goes over how to use the library and goes over the examples.
|
|
31
32
|
## Dependencies
|
|
32
33
|
|
|
33
34
|
- `pyarrow` (automatically installed)
|
|
34
35
|
- `pandas` (optional)
|
|
35
|
-
|
|
36
|
+
|
|
36
37
|
|
|
37
38
|
## Installation
|
|
38
39
|
|
|
@@ -113,7 +114,7 @@ write_options = WriteOptions(batch_size=500,
|
|
|
113
114
|
wco = write_client_options(success_callback=callback.success,
|
|
114
115
|
error_callback=callback.error,
|
|
115
116
|
retry_callback=callback.retry,
|
|
116
|
-
|
|
117
|
+
write_options=write_options
|
|
117
118
|
)
|
|
118
119
|
|
|
119
120
|
with InfluxDBClient3.InfluxDBClient3(
|
|
@@ -126,11 +127,11 @@ with InfluxDBClient3.InfluxDBClient3(
|
|
|
126
127
|
client.write_file(
|
|
127
128
|
file='./out.csv',
|
|
128
129
|
timestamp_column='time', tag_columns=["provider", "machineID"])
|
|
129
|
-
|
|
130
|
+
|
|
130
131
|
client.write_file(
|
|
131
132
|
file='./out.json',
|
|
132
133
|
timestamp_column='time', tag_columns=["provider", "machineID"], date_unit='ns' )
|
|
133
|
-
|
|
134
|
+
|
|
134
135
|
|
|
135
136
|
```
|
|
136
137
|
|
|
@@ -144,7 +145,7 @@ client._write_api.write(bucket="pokemon-codex", record=pd_df, data_frame_measure
|
|
|
144
145
|
client._write_api.write(bucket="pokemon-codex", record=pl_df, data_frame_measurement_name='caught', data_frame_tag_columns=['trainer', 'id', 'num'], data_frame_timestamp_column='timestamp')
|
|
145
146
|
```
|
|
146
147
|
|
|
147
|
-
## Querying
|
|
148
|
+
## Querying
|
|
148
149
|
|
|
149
150
|
### Querying with SQL
|
|
150
151
|
```python
|
|
@@ -196,4 +197,4 @@ table = client.query(
|
|
|
196
197
|
|
|
197
198
|
print(table.to_pandas())
|
|
198
199
|
```
|
|
199
|
-
You may also include your own root certificate via this manor aswell.
|
|
200
|
+
You may also include your own root certificate via this manor aswell.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: influxdb3-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -29,6 +29,9 @@ Requires-Dist: polars; extra == "polars"
|
|
|
29
29
|
Provides-Extra: dataframe
|
|
30
30
|
Requires-Dist: pandas; extra == "dataframe"
|
|
31
31
|
Requires-Dist: polars; extra == "dataframe"
|
|
32
|
+
Provides-Extra: test
|
|
33
|
+
Requires-Dist: pytest; extra == "test"
|
|
34
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
32
35
|
|
|
33
36
|
<p align="center">
|
|
34
37
|
<img src="https://github.com/InfluxCommunity/influxdb3-python/blob/main/python-logo.png?raw=true" alt="Your Image" width="150px">
|
|
@@ -60,11 +63,12 @@ Requires-Dist: polars; extra == "dataframe"
|
|
|
60
63
|
|
|
61
64
|
`influxdb_client_3` is a Python module that provides a simple and convenient way to interact with InfluxDB 3.0. This module supports both writing data to InfluxDB and querying data using the Flight client, which allows you to execute SQL and InfluxQL queries on InfluxDB 3.0.
|
|
62
65
|
|
|
66
|
+
We offer a ["Getting Started: InfluxDB 3.0 Python Client Library"](https://www.youtube.com/watch?v=tpdONTm1GC8) video that goes over how to use the library and goes over the examples.
|
|
63
67
|
## Dependencies
|
|
64
68
|
|
|
65
69
|
- `pyarrow` (automatically installed)
|
|
66
70
|
- `pandas` (optional)
|
|
67
|
-
|
|
71
|
+
|
|
68
72
|
|
|
69
73
|
## Installation
|
|
70
74
|
|
|
@@ -145,7 +149,7 @@ write_options = WriteOptions(batch_size=500,
|
|
|
145
149
|
wco = write_client_options(success_callback=callback.success,
|
|
146
150
|
error_callback=callback.error,
|
|
147
151
|
retry_callback=callback.retry,
|
|
148
|
-
|
|
152
|
+
write_options=write_options
|
|
149
153
|
)
|
|
150
154
|
|
|
151
155
|
with InfluxDBClient3.InfluxDBClient3(
|
|
@@ -158,11 +162,11 @@ with InfluxDBClient3.InfluxDBClient3(
|
|
|
158
162
|
client.write_file(
|
|
159
163
|
file='./out.csv',
|
|
160
164
|
timestamp_column='time', tag_columns=["provider", "machineID"])
|
|
161
|
-
|
|
165
|
+
|
|
162
166
|
client.write_file(
|
|
163
167
|
file='./out.json',
|
|
164
168
|
timestamp_column='time', tag_columns=["provider", "machineID"], date_unit='ns' )
|
|
165
|
-
|
|
169
|
+
|
|
166
170
|
|
|
167
171
|
```
|
|
168
172
|
|
|
@@ -176,7 +180,7 @@ client._write_api.write(bucket="pokemon-codex", record=pd_df, data_frame_measure
|
|
|
176
180
|
client._write_api.write(bucket="pokemon-codex", record=pl_df, data_frame_measurement_name='caught', data_frame_tag_columns=['trainer', 'id', 'num'], data_frame_timestamp_column='timestamp')
|
|
177
181
|
```
|
|
178
182
|
|
|
179
|
-
## Querying
|
|
183
|
+
## Querying
|
|
180
184
|
|
|
181
185
|
### Querying with SQL
|
|
182
186
|
```python
|
|
@@ -228,4 +232,4 @@ table = client.query(
|
|
|
228
232
|
|
|
229
233
|
print(table.to_pandas())
|
|
230
234
|
```
|
|
231
|
-
You may also include your own root certificate via this manor aswell.
|
|
235
|
+
You may also include your own root certificate via this manor aswell.
|
|
@@ -7,12 +7,13 @@ influxdb3_python.egg-info/dependency_links.txt
|
|
|
7
7
|
influxdb3_python.egg-info/requires.txt
|
|
8
8
|
influxdb3_python.egg-info/top_level.txt
|
|
9
9
|
influxdb_client_3/__init__.py
|
|
10
|
+
influxdb_client_3/py.typed
|
|
10
11
|
influxdb_client_3/read_file.py
|
|
12
|
+
influxdb_client_3/version.py
|
|
11
13
|
influxdb_client_3/write_client/__init__.py
|
|
12
14
|
influxdb_client_3/write_client/configuration.py
|
|
13
15
|
influxdb_client_3/write_client/extras.py
|
|
14
16
|
influxdb_client_3/write_client/rest.py
|
|
15
|
-
influxdb_client_3/write_client/version.py
|
|
16
17
|
influxdb_client_3/write_client/_sync/__init__.py
|
|
17
18
|
influxdb_client_3/write_client/_sync/api_client.py
|
|
18
19
|
influxdb_client_3/write_client/_sync/rest.py
|
|
@@ -39,5 +40,12 @@ influxdb_client_3/write_client/service/_base_service.py
|
|
|
39
40
|
influxdb_client_3/write_client/service/signin_service.py
|
|
40
41
|
influxdb_client_3/write_client/service/signout_service.py
|
|
41
42
|
influxdb_client_3/write_client/service/write_service.py
|
|
43
|
+
tests/test_api_client.py
|
|
42
44
|
tests/test_dataframe_serializer.py
|
|
43
|
-
tests/
|
|
45
|
+
tests/test_date_helper.py
|
|
46
|
+
tests/test_deep_merge.py
|
|
47
|
+
tests/test_influxdb_client_3.py
|
|
48
|
+
tests/test_influxdb_client_3_integration.py
|
|
49
|
+
tests/test_merge_options.py
|
|
50
|
+
tests/test_point.py
|
|
51
|
+
tests/test_query.py
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import urllib.parse
|
|
3
2
|
|
|
4
3
|
import pyarrow as pa
|
|
5
|
-
|
|
4
|
+
import importlib.util
|
|
6
5
|
|
|
6
|
+
from influxdb_client_3.query.query_api import QueryApi as _QueryApi
|
|
7
7
|
from influxdb_client_3.read_file import UploadFile
|
|
8
8
|
from influxdb_client_3.write_client import InfluxDBClient as _InfluxDBClient, WriteOptions, Point
|
|
9
9
|
from influxdb_client_3.write_client.client.exceptions import InfluxDBError
|
|
@@ -11,12 +11,7 @@ from influxdb_client_3.write_client.client.write_api import WriteApi as _WriteAp
|
|
|
11
11
|
PointSettings
|
|
12
12
|
from influxdb_client_3.write_client.domain.write_precision import WritePrecision
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
import polars as pl
|
|
16
|
-
|
|
17
|
-
polars = True
|
|
18
|
-
except ImportError:
|
|
19
|
-
polars = False
|
|
14
|
+
polars = importlib.util.find_spec("polars") is not None
|
|
20
15
|
|
|
21
16
|
|
|
22
17
|
def write_client_options(**kwargs):
|
|
@@ -69,12 +64,26 @@ def _deep_merge(target, source):
|
|
|
69
64
|
elif isinstance(target, list) and isinstance(source, list):
|
|
70
65
|
# If both target and source are lists, concatenate them
|
|
71
66
|
target.extend(source)
|
|
72
|
-
|
|
67
|
+
elif source is not None:
|
|
73
68
|
# For other types, simply replace the target with the source
|
|
74
69
|
target = source
|
|
75
70
|
return target
|
|
76
71
|
|
|
77
72
|
|
|
73
|
+
def _merge_options(defaults, exclude_keys=None, custom=None):
|
|
74
|
+
"""
|
|
75
|
+
Merge default option arguments with custom (user-provided) arguments,
|
|
76
|
+
excluding specific keys defined in exclude_keys.
|
|
77
|
+
"""
|
|
78
|
+
if custom is None or len(custom) == 0:
|
|
79
|
+
return defaults
|
|
80
|
+
|
|
81
|
+
if exclude_keys is None:
|
|
82
|
+
exclude_keys = []
|
|
83
|
+
|
|
84
|
+
return _deep_merge(defaults, {key: value for key, value in custom.items() if key not in exclude_keys})
|
|
85
|
+
|
|
86
|
+
|
|
78
87
|
class InfluxDBClient3:
|
|
79
88
|
def __init__(
|
|
80
89
|
self,
|
|
@@ -129,19 +138,15 @@ class InfluxDBClient3:
|
|
|
129
138
|
**kwargs)
|
|
130
139
|
|
|
131
140
|
self._write_api = _WriteApi(influxdb_client=self._client, **self._write_client_options)
|
|
132
|
-
self._flight_client_options = flight_client_options or {}
|
|
133
141
|
|
|
134
142
|
if query_port_overwrite is not None:
|
|
135
143
|
port = query_port_overwrite
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
if len(custom) == 0:
|
|
143
|
-
return defaults
|
|
144
|
-
return _deep_merge(defaults, {key: value for key, value in custom.items()})
|
|
144
|
+
if scheme == 'https':
|
|
145
|
+
connection_string = f"grpc+tls://{hostname}:{port}"
|
|
146
|
+
else:
|
|
147
|
+
connection_string = f"grpc+tcp://{hostname}:{port}"
|
|
148
|
+
self._query_api = _QueryApi(connection_string=connection_string, token=token,
|
|
149
|
+
flight_client_options=flight_client_options)
|
|
145
150
|
|
|
146
151
|
def write(self, record=None, database=None, **kwargs):
|
|
147
152
|
"""
|
|
@@ -214,20 +219,23 @@ class InfluxDBClient3:
|
|
|
214
219
|
data_frame_tag_columns=tag_columns,
|
|
215
220
|
data_frame_timestamp_column=timestamp_column, **kwargs)
|
|
216
221
|
|
|
217
|
-
def query(self, query, language="sql", mode="all", database=None, **kwargs):
|
|
218
|
-
"""
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
:
|
|
225
|
-
:param
|
|
226
|
-
:
|
|
222
|
+
def query(self, query: str, language: str = "sql", mode: str = "all", database: str = None, **kwargs):
|
|
223
|
+
"""Query data from InfluxDB.
|
|
224
|
+
|
|
225
|
+
If you want to use query parameters, you can pass them as kwargs:
|
|
226
|
+
|
|
227
|
+
>>> client.query("select * from cpu where host=$host", query_parameters={"host": "server01"})
|
|
228
|
+
|
|
229
|
+
:param query: The query to execute on the database.
|
|
230
|
+
:param language: The query language to use. It should be one of "influxql" or "sql". Defaults to "sql".
|
|
231
|
+
:param mode: The mode to use for the query. It should be one of "all", "pandas", "polars", "chunk",
|
|
232
|
+
"reader" or "schema". Defaults to "all".
|
|
227
233
|
:param database: The database to query from. If not provided, uses the database provided during initialization.
|
|
228
|
-
:
|
|
229
|
-
|
|
230
|
-
:
|
|
234
|
+
:param kwargs: Additional arguments to pass to the ``FlightCallOptions headers``. For example, it can be used to
|
|
235
|
+
set up per request headers.
|
|
236
|
+
:keyword query_parameters: The query parameters to use in the query.
|
|
237
|
+
It should be a ``dictionary`` of key-value pairs.
|
|
238
|
+
:return: The query result in the specified mode.
|
|
231
239
|
"""
|
|
232
240
|
if mode == "polars" and polars is False:
|
|
233
241
|
raise ImportError("Polars is not installed. Please install it with `pip install polars`.")
|
|
@@ -236,36 +244,14 @@ class InfluxDBClient3:
|
|
|
236
244
|
database = self._database
|
|
237
245
|
|
|
238
246
|
try:
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
"headers": [(b"authorization", f"Bearer {self._token}".encode('utf-8'))],
|
|
242
|
-
"timeout": 300
|
|
243
|
-
}
|
|
244
|
-
opts = self._merge_options(optargs, kwargs)
|
|
245
|
-
_options = FlightCallOptions(**opts)
|
|
246
|
-
|
|
247
|
-
ticket_data = {"database": database, "sql_query": query, "query_type": language}
|
|
248
|
-
ticket = Ticket(json.dumps(ticket_data).encode('utf-8'))
|
|
249
|
-
flight_reader = self._flight_client.do_get(ticket, _options)
|
|
250
|
-
|
|
251
|
-
mode_func = {
|
|
252
|
-
"all": flight_reader.read_all,
|
|
253
|
-
"pandas": flight_reader.read_pandas,
|
|
254
|
-
"polars": lambda: pl.from_arrow(flight_reader.read_all()),
|
|
255
|
-
"chunk": lambda: flight_reader,
|
|
256
|
-
"reader": flight_reader.to_reader,
|
|
257
|
-
"schema": lambda: flight_reader.schema
|
|
258
|
-
|
|
259
|
-
}.get(mode, flight_reader.read_all)
|
|
260
|
-
|
|
261
|
-
return mode_func() if callable(mode_func) else mode_func
|
|
262
|
-
except Exception as e:
|
|
247
|
+
return self._query_api.query(query=query, language=language, mode=mode, database=database, **kwargs)
|
|
248
|
+
except InfluxDBError as e:
|
|
263
249
|
raise e
|
|
264
250
|
|
|
265
251
|
def close(self):
|
|
266
252
|
"""Close the client and clean up resources."""
|
|
267
253
|
self._write_api.close()
|
|
268
|
-
self.
|
|
254
|
+
self._query_api.close()
|
|
269
255
|
self._client.close()
|
|
270
256
|
|
|
271
257
|
def __enter__(self):
|
|
File without changes
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/__init__.py
RENAMED
|
@@ -27,5 +27,5 @@ from influxdb_client_3.write_client.service.signout_service import SignoutServic
|
|
|
27
27
|
from influxdb_client_3.write_client.domain.write_precision import WritePrecision
|
|
28
28
|
|
|
29
29
|
from influxdb_client_3.write_client.configuration import Configuration
|
|
30
|
-
from influxdb_client_3.
|
|
30
|
+
from influxdb_client_3.version import VERSION
|
|
31
31
|
__version__ = VERSION
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/_sync/api_client.py
RENAMED
|
@@ -76,8 +76,8 @@ class ApiClient(object):
|
|
|
76
76
|
self.default_headers[header_name] = header_value
|
|
77
77
|
self.cookie = cookie
|
|
78
78
|
# Set default User-Agent.
|
|
79
|
-
from influxdb_client_3.
|
|
80
|
-
self.user_agent =
|
|
79
|
+
from influxdb_client_3.version import USER_AGENT
|
|
80
|
+
self.user_agent = USER_AGENT
|
|
81
81
|
|
|
82
82
|
def __del__(self):
|
|
83
83
|
"""Dispose pools."""
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import datetime
|
|
3
3
|
import threading
|
|
4
4
|
from datetime import timezone as tz
|
|
5
|
+
from sys import version_info
|
|
5
6
|
|
|
6
7
|
from dateutil import parser
|
|
7
8
|
|
|
@@ -90,7 +91,10 @@ def get_date_helper() -> DateHelper:
|
|
|
90
91
|
import ciso8601
|
|
91
92
|
_date_helper.parse_date = ciso8601.parse_datetime
|
|
92
93
|
except ModuleNotFoundError:
|
|
93
|
-
|
|
94
|
+
if (version_info.major, version_info.minor) >= (3, 11):
|
|
95
|
+
_date_helper.parse_date = datetime.datetime.fromisoformat
|
|
96
|
+
else:
|
|
97
|
+
_date_helper.parse_date = parser.parse
|
|
94
98
|
date_helper = _date_helper
|
|
95
99
|
|
|
96
100
|
return date_helper
|
|
@@ -10,7 +10,7 @@ from numbers import Integral
|
|
|
10
10
|
from influxdb_client_3.write_client.client.util.date_utils import get_date_helper
|
|
11
11
|
from influxdb_client_3.write_client.domain.write_precision import WritePrecision
|
|
12
12
|
|
|
13
|
-
EPOCH = datetime.
|
|
13
|
+
EPOCH = datetime.fromtimestamp(0, tz=timezone.utc)
|
|
14
14
|
|
|
15
15
|
DEFAULT_WRITE_PRECISION = WritePrecision.NS
|
|
16
16
|
|
|
@@ -2,7 +2,6 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
import os
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
requires = [
|
|
7
6
|
'reactivex >= 4.0.4',
|
|
8
7
|
'certifi >= 14.05.14',
|
|
@@ -15,6 +14,7 @@ requires = [
|
|
|
15
14
|
with open("./README.md", "r", encoding="utf-8") as fh:
|
|
16
15
|
long_description = fh.read()
|
|
17
16
|
|
|
17
|
+
|
|
18
18
|
def get_version_from_github_ref():
|
|
19
19
|
github_ref = os.environ.get("GITHUB_REF")
|
|
20
20
|
if not github_ref:
|
|
@@ -26,6 +26,7 @@ def get_version_from_github_ref():
|
|
|
26
26
|
|
|
27
27
|
return match.group(1)
|
|
28
28
|
|
|
29
|
+
|
|
29
30
|
def get_version():
|
|
30
31
|
# If running in GitHub Actions, get version from GITHUB_REF
|
|
31
32
|
version = get_version_from_github_ref()
|
|
@@ -35,6 +36,7 @@ def get_version():
|
|
|
35
36
|
# Fallback to a default version if not in GitHub Actions
|
|
36
37
|
return "v0.0.0"
|
|
37
38
|
|
|
39
|
+
|
|
38
40
|
setup(
|
|
39
41
|
name='influxdb3-python',
|
|
40
42
|
version=get_version(),
|
|
@@ -45,7 +47,12 @@ setup(
|
|
|
45
47
|
author_email='contact@influxdata.com',
|
|
46
48
|
url='https://github.com/InfluxCommunity/influxdb3-python',
|
|
47
49
|
packages=find_packages(exclude=['tests', 'tests.*', 'examples', 'examples.*']),
|
|
48
|
-
extras_require={
|
|
50
|
+
extras_require={
|
|
51
|
+
'pandas': ['pandas'],
|
|
52
|
+
'polars': ['polars'],
|
|
53
|
+
'dataframe': ['pandas', 'polars'],
|
|
54
|
+
'test': ['pytest', 'pytest-cov']
|
|
55
|
+
},
|
|
49
56
|
install_requires=requires,
|
|
50
57
|
python_requires='>=3.8',
|
|
51
58
|
classifiers=[
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest import mock
|
|
3
|
+
|
|
4
|
+
from influxdb_client_3.write_client._sync.api_client import ApiClient
|
|
5
|
+
from influxdb_client_3.write_client.configuration import Configuration
|
|
6
|
+
from influxdb_client_3.write_client.service import WriteService
|
|
7
|
+
from influxdb_client_3.version import VERSION
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_package = "influxdb3-python"
|
|
11
|
+
_sentHeaders = {}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def mock_rest_request(method,
|
|
15
|
+
url,
|
|
16
|
+
query_params=None,
|
|
17
|
+
headers=None,
|
|
18
|
+
body=None,
|
|
19
|
+
post_params=None,
|
|
20
|
+
_preload_content=True,
|
|
21
|
+
_request_timeout=None,
|
|
22
|
+
**urlopen_kw):
|
|
23
|
+
class MockResponse:
|
|
24
|
+
def __init__(self, data, status_code):
|
|
25
|
+
self.data = data
|
|
26
|
+
self.status_code = status_code
|
|
27
|
+
|
|
28
|
+
def data(self):
|
|
29
|
+
return self.data
|
|
30
|
+
|
|
31
|
+
global _sentHeaders
|
|
32
|
+
_sentHeaders = headers
|
|
33
|
+
|
|
34
|
+
return MockResponse(None, 200)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ApiClientTests(unittest.TestCase):
|
|
38
|
+
|
|
39
|
+
def test_default_headers(self):
|
|
40
|
+
global _package
|
|
41
|
+
conf = Configuration()
|
|
42
|
+
client = ApiClient(conf,
|
|
43
|
+
header_name="Authorization",
|
|
44
|
+
header_value="Bearer TEST_TOKEN")
|
|
45
|
+
self.assertIsNotNone(client.default_headers["User-Agent"])
|
|
46
|
+
self.assertIsNotNone(client.default_headers["Authorization"])
|
|
47
|
+
self.assertEqual(f"{_package}/{VERSION}", client.default_headers["User-Agent"])
|
|
48
|
+
self.assertEqual("Bearer TEST_TOKEN", client.default_headers["Authorization"])
|
|
49
|
+
|
|
50
|
+
@mock.patch("influxdb_client_3.write_client._sync.rest.RESTClientObject.request",
|
|
51
|
+
side_effect=mock_rest_request)
|
|
52
|
+
def test_call_api(self, mock_post):
|
|
53
|
+
global _package
|
|
54
|
+
global _sentHeaders
|
|
55
|
+
_sentHeaders = {}
|
|
56
|
+
|
|
57
|
+
conf = Configuration()
|
|
58
|
+
client = ApiClient(conf,
|
|
59
|
+
header_name="Authorization",
|
|
60
|
+
header_value="Bearer TEST_TOKEN")
|
|
61
|
+
service = WriteService(client)
|
|
62
|
+
service.post_write("TEST_ORG", "TEST_BUCKET", "data,foo=bar val=3.14")
|
|
63
|
+
self.assertEqual(4, len(_sentHeaders.keys()))
|
|
64
|
+
self.assertIsNotNone(_sentHeaders["Accept"])
|
|
65
|
+
self.assertEqual("application/json", _sentHeaders["Accept"])
|
|
66
|
+
self.assertIsNotNone(_sentHeaders["Content-Type"])
|
|
67
|
+
self.assertEqual("text/plain", _sentHeaders["Content-Type"])
|
|
68
|
+
self.assertIsNotNone(_sentHeaders["Authorization"])
|
|
69
|
+
self.assertEqual("Bearer TEST_TOKEN", _sentHeaders["Authorization"])
|
|
70
|
+
self.assertIsNotNone(_sentHeaders["User-Agent"])
|
|
71
|
+
self.assertEqual(f"{_package}/{VERSION}", _sentHeaders["User-Agent"])
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
|
|
4
|
+
from dateutil import tz
|
|
5
|
+
|
|
6
|
+
from influxdb_client_3.write_client.client.util.date_utils import DateHelper, get_date_helper
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestDateHelper(unittest.TestCase):
|
|
10
|
+
|
|
11
|
+
def test_to_utc(self):
|
|
12
|
+
date = get_date_helper().to_utc(datetime(2021, 4, 29, 20, 30, 10, 0))
|
|
13
|
+
self.assertEqual(datetime(2021, 4, 29, 20, 30, 10, 0, timezone.utc), date)
|
|
14
|
+
|
|
15
|
+
def test_to_utc_different_timezone(self):
|
|
16
|
+
date = DateHelper(timezone=tz.gettz('ETC/GMT+2')).to_utc(datetime(2021, 4, 29, 20, 30, 10, 0))
|
|
17
|
+
self.assertEqual(datetime(2021, 4, 29, 22, 30, 10, 0, timezone.utc), date)
|
|
18
|
+
|
|
19
|
+
def test_parse(self):
|
|
20
|
+
date = get_date_helper().parse_date("2021-03-20T15:59:10.607352Z")
|
|
21
|
+
self.assertEqual(datetime(2021, 3, 20, 15, 59, 10, 607352, timezone.utc), date)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
import influxdb_client_3
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestDeepMerge(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def test_deep_merge_dicts_with_no_overlap(self):
|
|
9
|
+
target = {"a": 1, "b": 2}
|
|
10
|
+
source = {"c": 3, "d": 4}
|
|
11
|
+
result = influxdb_client_3._deep_merge(target, source)
|
|
12
|
+
self.assertEqual(result, {"a": 1, "b": 2, "c": 3, "d": 4})
|
|
13
|
+
|
|
14
|
+
def test_deep_merge_dicts_with_overlap(self):
|
|
15
|
+
target = {"a": 1, "b": 2}
|
|
16
|
+
source = {"b": 3, "c": 4}
|
|
17
|
+
result = influxdb_client_3._deep_merge(target, source)
|
|
18
|
+
self.assertEqual(result, {"a": 1, "b": 3, "c": 4})
|
|
19
|
+
|
|
20
|
+
def test_deep_merge_nested_dicts(self):
|
|
21
|
+
target = {"a": {"b": 1}}
|
|
22
|
+
source = {"a": {"c": 2}}
|
|
23
|
+
result = influxdb_client_3._deep_merge(target, source)
|
|
24
|
+
self.assertEqual(result, {"a": {"b": 1, "c": 2}})
|
|
25
|
+
|
|
26
|
+
def test_deep_merge_lists(self):
|
|
27
|
+
target = [1, 2]
|
|
28
|
+
source = [3, 4]
|
|
29
|
+
result = influxdb_client_3._deep_merge(target, source)
|
|
30
|
+
self.assertEqual(result, [1, 2, 3, 4])
|
|
31
|
+
|
|
32
|
+
def test_deep_merge_non_overlapping_types(self):
|
|
33
|
+
target = {"a": 1}
|
|
34
|
+
source = [2, 3]
|
|
35
|
+
result = influxdb_client_3._deep_merge(target, source)
|
|
36
|
+
self.assertEqual(result, [2, 3])
|
|
37
|
+
|
|
38
|
+
def test_deep_merge_none_to_flight(self):
|
|
39
|
+
target = {
|
|
40
|
+
"headers": [(b"authorization", "Bearer xyz".encode('utf-8'))],
|
|
41
|
+
"timeout": 300
|
|
42
|
+
}
|
|
43
|
+
source = None
|
|
44
|
+
result = influxdb_client_3._deep_merge(target, source)
|
|
45
|
+
self.assertEqual(result, target)
|
|
46
|
+
|
|
47
|
+
def test_deep_merge_empty_to_flight(self):
|
|
48
|
+
target = {
|
|
49
|
+
"headers": [(b"authorization", "Bearer xyz".encode('utf-8'))],
|
|
50
|
+
"timeout": 300
|
|
51
|
+
}
|
|
52
|
+
source = {}
|
|
53
|
+
result = influxdb_client_3._deep_merge(target, source)
|
|
54
|
+
self.assertEqual(result, target)
|
|
@@ -8,11 +8,11 @@ class TestInfluxDBClient3(unittest.TestCase):
|
|
|
8
8
|
|
|
9
9
|
@patch('influxdb_client_3._InfluxDBClient')
|
|
10
10
|
@patch('influxdb_client_3._WriteApi')
|
|
11
|
-
@patch('influxdb_client_3.
|
|
12
|
-
def setUp(self,
|
|
11
|
+
@patch('influxdb_client_3._QueryApi')
|
|
12
|
+
def setUp(self, mock_query_api, mock_write_api, mock_influx_db_client):
|
|
13
13
|
self.mock_influx_db_client = mock_influx_db_client
|
|
14
14
|
self.mock_write_api = mock_write_api
|
|
15
|
-
self.
|
|
15
|
+
self.mock_query_api = mock_query_api
|
|
16
16
|
self.client = InfluxDBClient3(
|
|
17
17
|
host="localhost",
|
|
18
18
|
org="my_org",
|
|
@@ -25,7 +25,7 @@ class TestInfluxDBClient3(unittest.TestCase):
|
|
|
25
25
|
self.assertEqual(self.client._database, "my_db")
|
|
26
26
|
self.assertEqual(self.client._client, self.mock_influx_db_client.return_value)
|
|
27
27
|
self.assertEqual(self.client._write_api, self.mock_write_api.return_value)
|
|
28
|
-
self.assertEqual(self.client.
|
|
28
|
+
self.assertEqual(self.client._query_api, self.mock_query_api.return_value)
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
if __name__ == '__main__':
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from influxdb_client_3 import InfluxDBClient3
|
|
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
|
+
host = os.getenv('TESTING_INFLUXDB_URL')
|
|
25
|
+
token = os.getenv('TESTING_INFLUXDB_TOKEN')
|
|
26
|
+
database = os.getenv('TESTING_INFLUXDB_DATABASE')
|
|
27
|
+
|
|
28
|
+
self.client = InfluxDBClient3(host=host, database=database, token=token)
|
|
29
|
+
|
|
30
|
+
def tearDown(self):
|
|
31
|
+
if self.client:
|
|
32
|
+
self.client.close()
|
|
33
|
+
|
|
34
|
+
def test_write_and_query(self):
|
|
35
|
+
test_id = time.time_ns()
|
|
36
|
+
self.client.write(f"integration_test_python,type=used value=123.0,test_id={test_id}i")
|
|
37
|
+
|
|
38
|
+
sql = 'SELECT * FROM integration_test_python where type=$type and test_id=$test_id'
|
|
39
|
+
|
|
40
|
+
df = self.client.query(sql, mode="pandas", query_parameters={'type': 'used', 'test_id': test_id})
|
|
41
|
+
|
|
42
|
+
self.assertIsNotNone(df)
|
|
43
|
+
self.assertEqual(1, len(df))
|
|
44
|
+
self.assertEqual(test_id, df['test_id'][0])
|
|
45
|
+
self.assertEqual(123.0, df['value'][0])
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
import influxdb_client_3
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestMergeOptions(unittest.TestCase):
|
|
7
|
+
|
|
8
|
+
def test_merge_with_empty_custom(self):
|
|
9
|
+
defaults = {"a": 1, "b": 2}
|
|
10
|
+
result = influxdb_client_3._merge_options(defaults, custom={})
|
|
11
|
+
self.assertEqual(result, defaults)
|
|
12
|
+
|
|
13
|
+
def test_merge_with_none_custom(self):
|
|
14
|
+
defaults = {"a": 1, "b": 2}
|
|
15
|
+
result = influxdb_client_3._merge_options(defaults, custom=None)
|
|
16
|
+
self.assertEqual(result, defaults)
|
|
17
|
+
|
|
18
|
+
def test_merge_with_no_excluded_keys(self):
|
|
19
|
+
defaults = {"a": 1, "b": 2}
|
|
20
|
+
custom = {"b": 3, "c": 4}
|
|
21
|
+
result = influxdb_client_3._merge_options(defaults, custom=custom)
|
|
22
|
+
self.assertEqual(result, {"a": 1, "b": 3, "c": 4})
|
|
23
|
+
|
|
24
|
+
def test_merge_with_excluded_keys(self):
|
|
25
|
+
defaults = {"a": 1, "b": 2}
|
|
26
|
+
custom = {"b": 3, "c": 4}
|
|
27
|
+
result = influxdb_client_3._merge_options(defaults, exclude_keys=["b"], custom=custom)
|
|
28
|
+
self.assertEqual(result, {"a": 1, "b": 2, "c": 4})
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import unittest
|
|
3
|
+
|
|
4
|
+
from influxdb_client_3.write_client.client.write.point import EPOCH, Point
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TestPoint(unittest.TestCase):
|
|
8
|
+
|
|
9
|
+
def test_epoch(self):
|
|
10
|
+
self.assertEqual(EPOCH, datetime.datetime(1970, 1, 1, 0, 0, tzinfo=datetime.timezone.utc))
|
|
11
|
+
|
|
12
|
+
def test_point(self):
|
|
13
|
+
point = Point.measurement("h2o").tag("location", "europe").field("level", 2.2).time(1_000_000)
|
|
14
|
+
self.assertEqual('h2o,location=europe level=2.2 1000000', point.to_line_protocol())
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import struct
|
|
3
|
+
from unittest.mock import Mock, ANY
|
|
4
|
+
|
|
5
|
+
from pyarrow import (
|
|
6
|
+
array,
|
|
7
|
+
Table
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
from pyarrow.flight import (
|
|
11
|
+
FlightServerBase,
|
|
12
|
+
FlightUnauthenticatedError,
|
|
13
|
+
GeneratorStream,
|
|
14
|
+
ServerMiddleware,
|
|
15
|
+
ServerMiddlewareFactory,
|
|
16
|
+
ServerAuthHandler,
|
|
17
|
+
Ticket
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from influxdb_client_3 import InfluxDBClient3
|
|
21
|
+
from influxdb_client_3.version import USER_AGENT
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def case_insensitive_header_lookup(headers, lkey):
|
|
25
|
+
"""Lookup the value of a given key in the given headers.
|
|
26
|
+
The lkey is case-insensitive.
|
|
27
|
+
"""
|
|
28
|
+
for key in headers:
|
|
29
|
+
if key.lower() == lkey.lower():
|
|
30
|
+
return headers.get(key)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class NoopAuthHandler(ServerAuthHandler):
|
|
34
|
+
"""A no-op auth handler - as seen in pyarrow tests"""
|
|
35
|
+
|
|
36
|
+
def authenticate(self, outgoing, incoming):
|
|
37
|
+
"""Do nothing"""
|
|
38
|
+
|
|
39
|
+
def is_valid(self, token):
|
|
40
|
+
"""
|
|
41
|
+
Return an empty string
|
|
42
|
+
N.B. Returning None causes Type error
|
|
43
|
+
:param token:
|
|
44
|
+
:return:
|
|
45
|
+
"""
|
|
46
|
+
return ""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
_req_headers = {}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class HeaderCheckServerMiddlewareFactory(ServerMiddlewareFactory):
|
|
53
|
+
"""Factory to create HeaderCheckServerMiddleware and check header values"""
|
|
54
|
+
def start_call(self, info, headers):
|
|
55
|
+
auth_header = case_insensitive_header_lookup(headers, "Authorization")
|
|
56
|
+
values = auth_header[0].split(' ')
|
|
57
|
+
if values[0] != 'Bearer':
|
|
58
|
+
raise FlightUnauthenticatedError("Token required")
|
|
59
|
+
global _req_headers
|
|
60
|
+
_req_headers = headers
|
|
61
|
+
return HeaderCheckServerMiddleware(values[1])
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class HeaderCheckServerMiddleware(ServerMiddleware):
|
|
65
|
+
"""
|
|
66
|
+
Middleware needed to catch request headers via factory
|
|
67
|
+
N.B. As found in pyarrow tests
|
|
68
|
+
"""
|
|
69
|
+
def __init__(self, token, *args, **kwargs):
|
|
70
|
+
super().__init__(*args, **kwargs)
|
|
71
|
+
self.token = token
|
|
72
|
+
|
|
73
|
+
def sending_headers(self):
|
|
74
|
+
return {'authorization': 'Bearer ' + self.token}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class HeaderCheckFlightServer(FlightServerBase):
|
|
78
|
+
"""Mock server handle gRPC do_get calls"""
|
|
79
|
+
def do_get(self, context, ticket):
|
|
80
|
+
"""Return something to avoid needless errors"""
|
|
81
|
+
data = [
|
|
82
|
+
array([b"Vltava", struct.pack('<i', 105), b"FM"])
|
|
83
|
+
]
|
|
84
|
+
table = Table.from_arrays(data, names=['a'])
|
|
85
|
+
return GeneratorStream(
|
|
86
|
+
table.schema,
|
|
87
|
+
self.number_batches(table),
|
|
88
|
+
options={})
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def number_batches(table):
|
|
92
|
+
for idx, batch in enumerate(table.to_batches()):
|
|
93
|
+
buf = struct.pack('<i', idx)
|
|
94
|
+
yield batch, buf
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_influx_default_query_headers():
|
|
98
|
+
with HeaderCheckFlightServer(
|
|
99
|
+
auth_handler=NoopAuthHandler(),
|
|
100
|
+
middleware={"check": HeaderCheckServerMiddlewareFactory()}) as server:
|
|
101
|
+
global _req_headers
|
|
102
|
+
_req_headers = {}
|
|
103
|
+
client = InfluxDBClient3(
|
|
104
|
+
host=f'http://localhost:{server.port}',
|
|
105
|
+
org='test_org',
|
|
106
|
+
databse='test_db',
|
|
107
|
+
token='TEST_TOKEN'
|
|
108
|
+
)
|
|
109
|
+
client.query('SELECT * FROM test')
|
|
110
|
+
assert len(_req_headers) > 0
|
|
111
|
+
assert _req_headers['authorization'][0] == "Bearer TEST_TOKEN"
|
|
112
|
+
assert _req_headers['user-agent'][0].find(USER_AGENT) > -1
|
|
113
|
+
_req_headers = {}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class TestQuery(unittest.TestCase):
|
|
117
|
+
|
|
118
|
+
def setUp(self):
|
|
119
|
+
self.client = InfluxDBClient3(
|
|
120
|
+
host="localhost",
|
|
121
|
+
org="my_org",
|
|
122
|
+
database="my_db",
|
|
123
|
+
token="my_token"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def test_query_without_parameters(self):
|
|
127
|
+
mock_do_get = Mock()
|
|
128
|
+
self.client._query_api._do_get = mock_do_get
|
|
129
|
+
|
|
130
|
+
self.client.query('SELECT * FROM measurement')
|
|
131
|
+
|
|
132
|
+
expected_ticket = Ticket(
|
|
133
|
+
'{"database": "my_db", '
|
|
134
|
+
'"sql_query": "SELECT * FROM measurement", '
|
|
135
|
+
'"query_type": "sql"}'.encode('utf-8')
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
mock_do_get.assert_called_once_with(expected_ticket, ANY)
|
|
139
|
+
|
|
140
|
+
def test_query_with_parameters(self):
|
|
141
|
+
mock_do_get = Mock()
|
|
142
|
+
self.client._query_api._do_get = mock_do_get
|
|
143
|
+
|
|
144
|
+
self.client.query('SELECT * FROM measurement WHERE time > $time', query_parameters={"time": "2021-01-01"})
|
|
145
|
+
|
|
146
|
+
expected_ticket = Ticket(
|
|
147
|
+
'{"database": "my_db", '
|
|
148
|
+
'"sql_query": "SELECT * FROM measurement WHERE time > $time", '
|
|
149
|
+
'"query_type": "sql", "params": {"time": "2021-01-01"}}'.encode('utf-8')
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
mock_do_get.assert_called_once_with(expected_ticket, ANY)
|
|
File without changes
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb3_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/_sync/__init__.py
RENAMED
|
File without changes
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/_sync/rest.py
RENAMED
|
File without changes
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/__init__.py
RENAMED
|
File without changes
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/_base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/warnings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/client/write_api.py
RENAMED
|
File without changes
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/configuration.py
RENAMED
|
File without changes
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/domain/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{influxdb3_python-0.4.0 → influxdb3_python-0.6.0}/influxdb_client_3/write_client/service/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|