influxdb3-python 0.17.0__tar.gz → 0.19.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.19.0/Examples/query_with_middleware.py +33 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/PKG-INFO +31 -4
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/README.md +28 -1
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb3_python.egg-info/PKG-INFO +31 -4
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb3_python.egg-info/SOURCES.txt +1 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/exceptions/exceptions.py +32 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/query/query_api.py +14 -1
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/version.py +1 -1
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/_base.py +2 -1
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/write/dataframe_serializer.py +7 -6
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/write/point.py +50 -4
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/write/polars_dataframe_serializer.py +33 -22
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/write_api.py +19 -10
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/domain/write_precision.py +5 -5
- influxdb3_python-0.19.0/pyproject.toml +3 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/setup.py +2 -2
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_api_client.py +52 -2
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_dataframe_serializer.py +24 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_influxdb_client_3.py +63 -5
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_influxdb_client_3_integration.py +99 -2
- influxdb3_python-0.19.0/tests/test_point.py +67 -0
- influxdb3_python-0.19.0/tests/test_polars.py +213 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_query.py +45 -5
- influxdb3_python-0.17.0/pyproject.toml +0 -3
- influxdb3_python-0.17.0/tests/test_point.py +0 -14
- influxdb3_python-0.17.0/tests/test_polars.py +0 -101
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/__init__.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/basic_ssl_example.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/batching_example.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/cloud_dedicated_query.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/cloud_dedicated_write.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/config.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/flight_options_example.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/handle_http_error.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/handle_query_error.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/pandas_write.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/query_async.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/query_type.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/Examples/timeouts.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/LICENSE +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb3_python.egg-info/dependency_links.txt +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb3_python.egg-info/requires.txt +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb3_python.egg-info/top_level.txt +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/__init__.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/exceptions/__init__.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/py.typed +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/query/__init__.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/read_file.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/__init__.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/_sync/__init__.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/_sync/api_client.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/_sync/rest.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/__init__.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/influxdb_client.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/logging_handler.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/util/__init__.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/util/date_utils.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/util/date_utils_pandas.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/util/helpers.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/util/multiprocessing_helper.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/warnings.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/write/__init__.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/write/retry.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/configuration.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/domain/__init__.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/domain/write_precision_converter.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/extras.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/rest.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/service/__init__.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/service/_base_service.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/service/signin_service.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/service/signout_service.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/service/write_service.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/setup.cfg +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_date_helper.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_deep_merge.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_flush.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_merge_options.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_write_file.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_write_local_server.py +0 -0
- {influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/tests/test_write_precision_converter.py +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from pyarrow import flight
|
|
2
|
+
|
|
3
|
+
from config import Config
|
|
4
|
+
from influxdb_client_3 import InfluxDBClient3, flight_client_options
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# This middleware will add an additional attribute `some-attribute` to the header
|
|
8
|
+
class ModifyHeaderClientMiddleware(flight.ClientMiddleware):
|
|
9
|
+
def sending_headers(self):
|
|
10
|
+
return {
|
|
11
|
+
"some-attribute": "some-value",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
def received_headers(self, headers):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ModifyHeaderClientMiddlewareFactory(flight.ClientMiddlewareFactory):
|
|
19
|
+
def start_call(self, info):
|
|
20
|
+
return ModifyHeaderClientMiddleware()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
config = Config()
|
|
24
|
+
middleware = [ModifyHeaderClientMiddlewareFactory()]
|
|
25
|
+
client = InfluxDBClient3(
|
|
26
|
+
host=config.host,
|
|
27
|
+
token=config.token,
|
|
28
|
+
database=config.database,
|
|
29
|
+
flight_client_options=flight_client_options(middleware=middleware)
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
df = client.query(query="select * from cpu11 limit 10", mode="pandas")
|
|
33
|
+
print(len(df))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: influxdb3-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.19.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
|
|
@@ -8,13 +8,13 @@ Author-email: contact@influxdata.com
|
|
|
8
8
|
Classifier: Development Status :: 4 - Beta
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.9
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
-
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
Requires-Dist: reactivex>=4.0.4
|
|
@@ -53,6 +53,9 @@ Dynamic: summary
|
|
|
53
53
|
</p>
|
|
54
54
|
|
|
55
55
|
<p align="center">
|
|
56
|
+
<a href="https://influxdb3-python.readthedocs.io/en/latest/">
|
|
57
|
+
<img src="https://img.shields.io/readthedocs/influxdb3-python/latest" alt="Readthedocs document">
|
|
58
|
+
</a>
|
|
56
59
|
<a href="https://pypi.org/project/influxdb3-python/">
|
|
57
60
|
<img src="https://img.shields.io/pypi/v/influxdb3-python.svg" alt="PyPI version">
|
|
58
61
|
</a>
|
|
@@ -95,7 +98,7 @@ pip install influxdb3-python
|
|
|
95
98
|
|
|
96
99
|
Note: This does not include Pandas support. If you would like to use key features such as `to_pandas()` and `write_file()` you will need to install `pandas` separately.
|
|
97
100
|
|
|
98
|
-
*Note: Please make sure you are using 3.
|
|
101
|
+
*Note: Please make sure you are using 3.9 or above. For the best performance use 3.11+*
|
|
99
102
|
|
|
100
103
|
# Usage
|
|
101
104
|
One of the easiest ways to get started is to checkout the ["Pokemon Trainer Cookbook"](https://github.com/InfluxCommunity/influxdb3-python/blob/main/Examples/pokemon-trainer/cookbook.ipynb). This scenario takes you through the basics of both the client library and Pyarrow.
|
|
@@ -123,6 +126,30 @@ You can write data using the Point class, or supplying line protocol.
|
|
|
123
126
|
point = Point("measurement").tag("location", "london").field("temperature", 42)
|
|
124
127
|
client.write(point)
|
|
125
128
|
```
|
|
129
|
+
|
|
130
|
+
### Control tag order for first-write column order (InfluxDB 3 Enterprise)
|
|
131
|
+
```python
|
|
132
|
+
from influxdb_client_3 import InfluxDBClient3, Point, WriteOptions, WriteType, write_client_options
|
|
133
|
+
|
|
134
|
+
point = Point("cpu") \
|
|
135
|
+
.tag("host", "server-a") \
|
|
136
|
+
.tag("region", "us-east") \
|
|
137
|
+
.tag("rack", "r1") \
|
|
138
|
+
.field("usage", 0.42)
|
|
139
|
+
|
|
140
|
+
write_options = WriteOptions(
|
|
141
|
+
write_type=WriteType.synchronous,
|
|
142
|
+
tag_order=["region", "host"],
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
client = InfluxDBClient3(
|
|
146
|
+
token="your-token",
|
|
147
|
+
host="your-host",
|
|
148
|
+
database="your-database",
|
|
149
|
+
write_client_options=write_client_options(write_options=write_options),
|
|
150
|
+
)
|
|
151
|
+
client.write(point)
|
|
152
|
+
```
|
|
126
153
|
### Using Line Protocol
|
|
127
154
|
```python
|
|
128
155
|
point = "measurement fieldname=0"
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
</p>
|
|
5
5
|
|
|
6
6
|
<p align="center">
|
|
7
|
+
<a href="https://influxdb3-python.readthedocs.io/en/latest/">
|
|
8
|
+
<img src="https://img.shields.io/readthedocs/influxdb3-python/latest" alt="Readthedocs document">
|
|
9
|
+
</a>
|
|
7
10
|
<a href="https://pypi.org/project/influxdb3-python/">
|
|
8
11
|
<img src="https://img.shields.io/pypi/v/influxdb3-python.svg" alt="PyPI version">
|
|
9
12
|
</a>
|
|
@@ -46,7 +49,7 @@ pip install influxdb3-python
|
|
|
46
49
|
|
|
47
50
|
Note: This does not include Pandas support. If you would like to use key features such as `to_pandas()` and `write_file()` you will need to install `pandas` separately.
|
|
48
51
|
|
|
49
|
-
*Note: Please make sure you are using 3.
|
|
52
|
+
*Note: Please make sure you are using 3.9 or above. For the best performance use 3.11+*
|
|
50
53
|
|
|
51
54
|
# Usage
|
|
52
55
|
One of the easiest ways to get started is to checkout the ["Pokemon Trainer Cookbook"](https://github.com/InfluxCommunity/influxdb3-python/blob/main/Examples/pokemon-trainer/cookbook.ipynb). This scenario takes you through the basics of both the client library and Pyarrow.
|
|
@@ -74,6 +77,30 @@ You can write data using the Point class, or supplying line protocol.
|
|
|
74
77
|
point = Point("measurement").tag("location", "london").field("temperature", 42)
|
|
75
78
|
client.write(point)
|
|
76
79
|
```
|
|
80
|
+
|
|
81
|
+
### Control tag order for first-write column order (InfluxDB 3 Enterprise)
|
|
82
|
+
```python
|
|
83
|
+
from influxdb_client_3 import InfluxDBClient3, Point, WriteOptions, WriteType, write_client_options
|
|
84
|
+
|
|
85
|
+
point = Point("cpu") \
|
|
86
|
+
.tag("host", "server-a") \
|
|
87
|
+
.tag("region", "us-east") \
|
|
88
|
+
.tag("rack", "r1") \
|
|
89
|
+
.field("usage", 0.42)
|
|
90
|
+
|
|
91
|
+
write_options = WriteOptions(
|
|
92
|
+
write_type=WriteType.synchronous,
|
|
93
|
+
tag_order=["region", "host"],
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
client = InfluxDBClient3(
|
|
97
|
+
token="your-token",
|
|
98
|
+
host="your-host",
|
|
99
|
+
database="your-database",
|
|
100
|
+
write_client_options=write_client_options(write_options=write_options),
|
|
101
|
+
)
|
|
102
|
+
client.write(point)
|
|
103
|
+
```
|
|
77
104
|
### Using Line Protocol
|
|
78
105
|
```python
|
|
79
106
|
point = "measurement fieldname=0"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: influxdb3-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.19.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
|
|
@@ -8,13 +8,13 @@ Author-email: contact@influxdata.com
|
|
|
8
8
|
Classifier: Development Status :: 4 - Beta
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.9
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
-
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
19
19
|
License-File: LICENSE
|
|
20
20
|
Requires-Dist: reactivex>=4.0.4
|
|
@@ -53,6 +53,9 @@ Dynamic: summary
|
|
|
53
53
|
</p>
|
|
54
54
|
|
|
55
55
|
<p align="center">
|
|
56
|
+
<a href="https://influxdb3-python.readthedocs.io/en/latest/">
|
|
57
|
+
<img src="https://img.shields.io/readthedocs/influxdb3-python/latest" alt="Readthedocs document">
|
|
58
|
+
</a>
|
|
56
59
|
<a href="https://pypi.org/project/influxdb3-python/">
|
|
57
60
|
<img src="https://img.shields.io/pypi/v/influxdb3-python.svg" alt="PyPI version">
|
|
58
61
|
</a>
|
|
@@ -95,7 +98,7 @@ pip install influxdb3-python
|
|
|
95
98
|
|
|
96
99
|
Note: This does not include Pandas support. If you would like to use key features such as `to_pandas()` and `write_file()` you will need to install `pandas` separately.
|
|
97
100
|
|
|
98
|
-
*Note: Please make sure you are using 3.
|
|
101
|
+
*Note: Please make sure you are using 3.9 or above. For the best performance use 3.11+*
|
|
99
102
|
|
|
100
103
|
# Usage
|
|
101
104
|
One of the easiest ways to get started is to checkout the ["Pokemon Trainer Cookbook"](https://github.com/InfluxCommunity/influxdb3-python/blob/main/Examples/pokemon-trainer/cookbook.ipynb). This scenario takes you through the basics of both the client library and Pyarrow.
|
|
@@ -123,6 +126,30 @@ You can write data using the Point class, or supplying line protocol.
|
|
|
123
126
|
point = Point("measurement").tag("location", "london").field("temperature", 42)
|
|
124
127
|
client.write(point)
|
|
125
128
|
```
|
|
129
|
+
|
|
130
|
+
### Control tag order for first-write column order (InfluxDB 3 Enterprise)
|
|
131
|
+
```python
|
|
132
|
+
from influxdb_client_3 import InfluxDBClient3, Point, WriteOptions, WriteType, write_client_options
|
|
133
|
+
|
|
134
|
+
point = Point("cpu") \
|
|
135
|
+
.tag("host", "server-a") \
|
|
136
|
+
.tag("region", "us-east") \
|
|
137
|
+
.tag("rack", "r1") \
|
|
138
|
+
.field("usage", 0.42)
|
|
139
|
+
|
|
140
|
+
write_options = WriteOptions(
|
|
141
|
+
write_type=WriteType.synchronous,
|
|
142
|
+
tag_order=["region", "host"],
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
client = InfluxDBClient3(
|
|
146
|
+
token="your-token",
|
|
147
|
+
host="your-host",
|
|
148
|
+
database="your-database",
|
|
149
|
+
write_client_options=write_client_options(write_options=write_options),
|
|
150
|
+
)
|
|
151
|
+
client.write(point)
|
|
152
|
+
```
|
|
126
153
|
### Using Line Protocol
|
|
127
154
|
```python
|
|
128
155
|
point = "measurement fieldname=0"
|
{influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/exceptions/exceptions.py
RENAMED
|
@@ -63,9 +63,41 @@ class InfluxDBError(InfluxDB3ClientError):
|
|
|
63
63
|
def get(d, key):
|
|
64
64
|
if not key or d is None:
|
|
65
65
|
return d
|
|
66
|
+
if not isinstance(d, dict):
|
|
67
|
+
return None
|
|
66
68
|
return get(d.get(key[0]), key[1:])
|
|
67
69
|
try:
|
|
68
70
|
node = json.loads(response.data)
|
|
71
|
+
if isinstance(node, dict):
|
|
72
|
+
# InfluxDB v3 error format: { "code": "...", "message": "..." }
|
|
73
|
+
code = node.get("code")
|
|
74
|
+
message = node.get("message")
|
|
75
|
+
if message:
|
|
76
|
+
return f"{code}: {message}" if code else message
|
|
77
|
+
# InfluxDB v3 write error format:
|
|
78
|
+
# {
|
|
79
|
+
# "error": "...",
|
|
80
|
+
# "data": [ { "error_message": "...", "line_number": 2, "original_line": "..." }, ... ]
|
|
81
|
+
# }
|
|
82
|
+
error_text = node.get("error")
|
|
83
|
+
data = node.get("data")
|
|
84
|
+
if error_text and isinstance(data, list):
|
|
85
|
+
details = []
|
|
86
|
+
for item in data:
|
|
87
|
+
if not isinstance(item, dict):
|
|
88
|
+
continue
|
|
89
|
+
line_number = item.get("line_number")
|
|
90
|
+
error_message = item.get("error_message")
|
|
91
|
+
original_line = item.get("original_line")
|
|
92
|
+
if line_number is not None and error_message and original_line:
|
|
93
|
+
details.append(
|
|
94
|
+
f"\tline {line_number}: {error_message} ({original_line})"
|
|
95
|
+
)
|
|
96
|
+
elif error_message:
|
|
97
|
+
details.append(f"\t{error_message}")
|
|
98
|
+
if details:
|
|
99
|
+
return error_text + ":\n" + "\n".join(details)
|
|
100
|
+
return error_text
|
|
69
101
|
for key in [['message'], ['data', 'error_message'], ['error']]:
|
|
70
102
|
value = get(node, key)
|
|
71
103
|
if value is not None:
|
|
@@ -20,6 +20,7 @@ class QueryApiOptions(object):
|
|
|
20
20
|
flight_client_options (dict): base set of flight client options passed to internal pyarrow.flight.FlightClient
|
|
21
21
|
timeout(float): timeout in seconds to wait for a response
|
|
22
22
|
disable_grpc_compression (bool): disable gRPC compression for query responses
|
|
23
|
+
middleware (list): list of middleware functions to be applied to Flight calls
|
|
23
24
|
"""
|
|
24
25
|
_DEFAULT_TIMEOUT = 300.0
|
|
25
26
|
tls_root_certs: bytes = None
|
|
@@ -28,13 +29,15 @@ class QueryApiOptions(object):
|
|
|
28
29
|
flight_client_options: dict = None
|
|
29
30
|
timeout: float = None
|
|
30
31
|
disable_grpc_compression: bool = False
|
|
32
|
+
middleware: list = None
|
|
31
33
|
|
|
32
34
|
def __init__(self, root_certs_path: str,
|
|
33
35
|
verify: bool,
|
|
34
36
|
proxy: str,
|
|
35
37
|
flight_client_options: dict,
|
|
36
38
|
timeout: float = _DEFAULT_TIMEOUT,
|
|
37
|
-
disable_grpc_compression: bool = False
|
|
39
|
+
disable_grpc_compression: bool = False,
|
|
40
|
+
middleware: list = None):
|
|
38
41
|
"""
|
|
39
42
|
Initialize a set of QueryApiOptions
|
|
40
43
|
|
|
@@ -45,6 +48,7 @@ class QueryApiOptions(object):
|
|
|
45
48
|
to be passed to internal pyarrow.flight.FlightClient.
|
|
46
49
|
:param timeout: timeout in seconds to wait for a response.
|
|
47
50
|
:param disable_grpc_compression: disable gRPC compression for query responses.
|
|
51
|
+
:param middleware: list of middleware functions to be applied to Flight calls.
|
|
48
52
|
"""
|
|
49
53
|
if root_certs_path:
|
|
50
54
|
self.tls_root_certs = self._read_certs(root_certs_path)
|
|
@@ -53,6 +57,7 @@ class QueryApiOptions(object):
|
|
|
53
57
|
self.flight_client_options = flight_client_options
|
|
54
58
|
self.timeout = timeout
|
|
55
59
|
self.disable_grpc_compression = disable_grpc_compression
|
|
60
|
+
self.middleware = middleware
|
|
56
61
|
|
|
57
62
|
def _read_certs(self, path: str) -> bytes:
|
|
58
63
|
with open(path, "rb") as certs_file:
|
|
@@ -81,6 +86,7 @@ class QueryApiOptionsBuilder(object):
|
|
|
81
86
|
_flight_client_options: dict = None
|
|
82
87
|
_timeout: float = None
|
|
83
88
|
_disable_grpc_compression: bool = False
|
|
89
|
+
_middleware: list = None
|
|
84
90
|
|
|
85
91
|
def root_certs(self, path: str):
|
|
86
92
|
self._root_certs_path = path
|
|
@@ -107,6 +113,10 @@ class QueryApiOptionsBuilder(object):
|
|
|
107
113
|
self._disable_grpc_compression = disable
|
|
108
114
|
return self
|
|
109
115
|
|
|
116
|
+
def middleware(self, middleware: list):
|
|
117
|
+
self._middleware = middleware
|
|
118
|
+
return self
|
|
119
|
+
|
|
110
120
|
def build(self) -> QueryApiOptions:
|
|
111
121
|
"""Build a QueryApiOptions object with previously set values"""
|
|
112
122
|
return QueryApiOptions(
|
|
@@ -116,6 +126,7 @@ class QueryApiOptionsBuilder(object):
|
|
|
116
126
|
flight_client_options=self._flight_client_options,
|
|
117
127
|
timeout=self._timeout,
|
|
118
128
|
disable_grpc_compression=self._disable_grpc_compression,
|
|
129
|
+
middleware=self._middleware
|
|
119
130
|
)
|
|
120
131
|
|
|
121
132
|
|
|
@@ -181,6 +192,8 @@ class QueryApi(object):
|
|
|
181
192
|
self._flight_client_options["generic_options"].append(
|
|
182
193
|
("grpc.compression_enabled_algorithms_bitset", 1)
|
|
183
194
|
)
|
|
195
|
+
if options.middleware:
|
|
196
|
+
self._flight_client_options["middleware"] = options.middleware
|
|
184
197
|
if self._proxy:
|
|
185
198
|
self._flight_client_options["generic_options"].append(("grpc.http_proxy", self._proxy))
|
|
186
199
|
self._flight_client = FlightClient(connection_string, **self._flight_client_options)
|
{influxdb3_python-0.17.0 → influxdb3_python-0.19.0}/influxdb_client_3/write_client/client/_base.py
RENAMED
|
@@ -246,7 +246,8 @@ class _BaseWriteApi(object):
|
|
|
246
246
|
elif isinstance(record, Point):
|
|
247
247
|
precision_from_point = kwargs.get('precision_from_point', True)
|
|
248
248
|
precision = record.write_precision if precision_from_point else write_precision
|
|
249
|
-
self._serialize(record.to_line_protocol(precision=precision
|
|
249
|
+
self._serialize(record.to_line_protocol(precision=precision, tag_order=kwargs.get('tag_order')),
|
|
250
|
+
precision, payload, **kwargs)
|
|
250
251
|
|
|
251
252
|
elif isinstance(record, dict):
|
|
252
253
|
self._serialize(Point.from_dict(record, write_precision=write_precision, **kwargs),
|
|
@@ -10,7 +10,7 @@ import re
|
|
|
10
10
|
|
|
11
11
|
from influxdb_client_3.write_client.domain import WritePrecision
|
|
12
12
|
from influxdb_client_3.write_client.client.write.point import _ESCAPE_KEY, _ESCAPE_STRING, _ESCAPE_MEASUREMENT, \
|
|
13
|
-
DEFAULT_WRITE_PRECISION
|
|
13
|
+
DEFAULT_WRITE_PRECISION, ordered_tag_keys
|
|
14
14
|
|
|
15
15
|
logger = logging.getLogger('influxdb_client.client.write.dataframe_serializer')
|
|
16
16
|
|
|
@@ -130,8 +130,8 @@ class DataframeSerializer:
|
|
|
130
130
|
|
|
131
131
|
# keys holds a list of string keys.
|
|
132
132
|
keys = []
|
|
133
|
-
#
|
|
134
|
-
|
|
133
|
+
# tag_segments holds map of tag key -> tag f-string segment.
|
|
134
|
+
tag_segments = {}
|
|
135
135
|
# fields holds a list of field f-string segments ordered alphabetically by field key
|
|
136
136
|
fields = []
|
|
137
137
|
# field_indexes holds the index into each row of all the fields.
|
|
@@ -188,7 +188,7 @@ class DataframeSerializer:
|
|
|
188
188
|
}}"""
|
|
189
189
|
else:
|
|
190
190
|
key_value = f',{key_format}={{str({val_format}).translate(_ESCAPE_KEY)}}'
|
|
191
|
-
|
|
191
|
+
tag_segments[key] = key_value
|
|
192
192
|
continue
|
|
193
193
|
elif timestamp_column is not None and key in timestamp_column:
|
|
194
194
|
timestamp_index = field_index
|
|
@@ -225,7 +225,8 @@ class DataframeSerializer:
|
|
|
225
225
|
|
|
226
226
|
measurement_name = str(data_frame_measurement_name).translate(_ESCAPE_MEASUREMENT)
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
tag_keys = ordered_tag_keys(list(tag_segments.keys()), kwargs.get('tag_order'))
|
|
229
|
+
tag_string = ''.join(tag_segments[tag_key] for tag_key in tag_keys)
|
|
229
230
|
fields = ''.join(fields)
|
|
230
231
|
timestamp = '{p[%s].value}' % timestamp_index
|
|
231
232
|
if precision == WritePrecision.US:
|
|
@@ -235,7 +236,7 @@ class DataframeSerializer:
|
|
|
235
236
|
elif precision == WritePrecision.S:
|
|
236
237
|
timestamp = '{int(p[%s].value / 1e9)}' % timestamp_index
|
|
237
238
|
|
|
238
|
-
f = eval(f'lambda p: f"""{{measurement_name}}{
|
|
239
|
+
f = eval(f'lambda p: f"""{{measurement_name}}{tag_string} {fields} {timestamp}"""', {
|
|
239
240
|
'measurement_name': measurement_name,
|
|
240
241
|
'_ESCAPE_KEY': _ESCAPE_KEY,
|
|
241
242
|
'_ESCAPE_STRING': _ESCAPE_STRING,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import math
|
|
4
4
|
import warnings
|
|
5
5
|
from builtins import int
|
|
6
|
+
from collections.abc import Iterable
|
|
6
7
|
from datetime import datetime, timedelta, timezone
|
|
7
8
|
from decimal import Decimal
|
|
8
9
|
from numbers import Integral
|
|
@@ -215,11 +216,12 @@ class Point(object):
|
|
|
215
216
|
self._fields[field] = value
|
|
216
217
|
return self
|
|
217
218
|
|
|
218
|
-
def to_line_protocol(self, precision=None):
|
|
219
|
+
def to_line_protocol(self, precision=None, tag_order=None):
|
|
219
220
|
"""
|
|
220
221
|
Create LineProtocol.
|
|
221
222
|
|
|
222
223
|
:param precision: required precision of LineProtocol. If it's not set then use the precision from ``Point``.
|
|
224
|
+
:param tag_order: optional list of tag names to prioritize in serialized output
|
|
223
225
|
"""
|
|
224
226
|
_measurement = _escape_key(self._name, _ESCAPE_MEASUREMENT)
|
|
225
227
|
if _measurement.startswith("#"):
|
|
@@ -229,7 +231,7 @@ The output Line protocol will be interpret as a comment by InfluxDB. For more in
|
|
|
229
231
|
- https://docs.influxdata.com/influxdb/latest/reference/syntax/line-protocol/#comments
|
|
230
232
|
"""
|
|
231
233
|
warnings.warn(message, SyntaxWarning)
|
|
232
|
-
_tags = _append_tags(self._tags)
|
|
234
|
+
_tags = _append_tags(self._tags, tag_order)
|
|
233
235
|
_fields = _append_fields(self._fields, self._field_types)
|
|
234
236
|
if not _fields:
|
|
235
237
|
return ""
|
|
@@ -252,9 +254,10 @@ The output Line protocol will be interpret as a comment by InfluxDB. For more in
|
|
|
252
254
|
return self.to_line_protocol()
|
|
253
255
|
|
|
254
256
|
|
|
255
|
-
def _append_tags(tags):
|
|
257
|
+
def _append_tags(tags, tag_order=None):
|
|
256
258
|
_return = []
|
|
257
|
-
for tag_key
|
|
259
|
+
for tag_key in ordered_tag_keys(sorted(tags.keys()), tag_order):
|
|
260
|
+
tag_value = tags.get(tag_key)
|
|
258
261
|
|
|
259
262
|
if tag_value is None:
|
|
260
263
|
continue
|
|
@@ -267,6 +270,49 @@ def _append_tags(tags):
|
|
|
267
270
|
return f"{',' if _return else ''}{','.join(_return)} "
|
|
268
271
|
|
|
269
272
|
|
|
273
|
+
def sanitize_tag_order(tag_order):
|
|
274
|
+
if tag_order is None:
|
|
275
|
+
return []
|
|
276
|
+
|
|
277
|
+
if isinstance(tag_order, (str, bytes)):
|
|
278
|
+
raise TypeError("tag_order must be an iterable of strings, not str/bytes")
|
|
279
|
+
|
|
280
|
+
if not isinstance(tag_order, Iterable):
|
|
281
|
+
raise TypeError("tag_order must be an iterable of strings")
|
|
282
|
+
|
|
283
|
+
sanitized = []
|
|
284
|
+
seen = set()
|
|
285
|
+
for tag in tag_order:
|
|
286
|
+
if tag is None or tag == "":
|
|
287
|
+
continue
|
|
288
|
+
if not isinstance(tag, str):
|
|
289
|
+
raise TypeError("tag_order entries must be strings")
|
|
290
|
+
if tag in seen:
|
|
291
|
+
continue
|
|
292
|
+
seen.add(tag)
|
|
293
|
+
sanitized.append(tag)
|
|
294
|
+
return sanitized
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def ordered_tag_keys(existing_keys, tag_order=None):
|
|
298
|
+
ordered_keys = list(existing_keys)
|
|
299
|
+
if not tag_order:
|
|
300
|
+
return ordered_keys
|
|
301
|
+
|
|
302
|
+
remaining = set(ordered_keys)
|
|
303
|
+
prioritized = []
|
|
304
|
+
for tag_key in tag_order:
|
|
305
|
+
if not tag_key:
|
|
306
|
+
continue
|
|
307
|
+
if tag_key not in remaining:
|
|
308
|
+
continue
|
|
309
|
+
remaining.remove(tag_key)
|
|
310
|
+
prioritized.append(tag_key)
|
|
311
|
+
|
|
312
|
+
prioritized.extend([tag_key for tag_key in ordered_keys if tag_key in remaining])
|
|
313
|
+
return prioritized
|
|
314
|
+
|
|
315
|
+
|
|
270
316
|
def _append_fields(fields, field_types):
|
|
271
317
|
_return = []
|
|
272
318
|
|
|
@@ -7,7 +7,8 @@ Much of the code here is inspired by that in the aioinflux packet found here: ht
|
|
|
7
7
|
import logging
|
|
8
8
|
import math
|
|
9
9
|
|
|
10
|
-
from influxdb_client_3.write_client.client.write.point import _ESCAPE_KEY, _ESCAPE_STRING, DEFAULT_WRITE_PRECISION
|
|
10
|
+
from influxdb_client_3.write_client.client.write.point import _ESCAPE_KEY, _ESCAPE_STRING, DEFAULT_WRITE_PRECISION, \
|
|
11
|
+
ordered_tag_keys
|
|
11
12
|
|
|
12
13
|
logger = logging.getLogger('influxdb_client.client.write.polars_dataframe_serializer')
|
|
13
14
|
|
|
@@ -36,6 +37,7 @@ class PolarsDataframeSerializer:
|
|
|
36
37
|
self.chunk_size = chunk_size
|
|
37
38
|
self.measurement_name = kwargs.get("data_frame_measurement_name", "measurement")
|
|
38
39
|
self.tag_columns = kwargs.get("data_frame_tag_columns", [])
|
|
40
|
+
self.tag_order = kwargs.get("tag_order", None)
|
|
39
41
|
self.timestamp_column = kwargs.get("data_frame_timestamp_column", None)
|
|
40
42
|
self.timestamp_timezone = kwargs.get("data_frame_timestamp_timezone", None)
|
|
41
43
|
|
|
@@ -62,34 +64,43 @@ class PolarsDataframeSerializer:
|
|
|
62
64
|
return str(value).translate(_ESCAPE_STRING)
|
|
63
65
|
|
|
64
66
|
def to_line_protocol(self, row):
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
tag_values = {}
|
|
68
|
+
tag_keys = []
|
|
69
|
+
for col in self.tag_columns:
|
|
70
|
+
value = row[self.column_indices[col]]
|
|
71
|
+
if value is None or value == "":
|
|
72
|
+
continue
|
|
73
|
+
if col not in tag_values:
|
|
74
|
+
tag_keys.append(col)
|
|
75
|
+
tag_values[col] = value
|
|
67
76
|
|
|
77
|
+
if self.point_settings.defaultTags:
|
|
78
|
+
for key, value in self.point_settings.defaultTags.items():
|
|
79
|
+
if value is None or value == "":
|
|
80
|
+
continue
|
|
81
|
+
if key in tag_values:
|
|
82
|
+
continue
|
|
83
|
+
tag_keys.append(key)
|
|
84
|
+
tag_values[key] = value
|
|
85
|
+
|
|
86
|
+
final_tag_keys = ordered_tag_keys(tag_keys, self.tag_order)
|
|
68
87
|
tags = ",".join(
|
|
69
|
-
f'{self.escape_key(
|
|
70
|
-
for
|
|
71
|
-
if row[self.column_indices[col]] is not None and row[self.column_indices[col]] != ""
|
|
88
|
+
f'{self.escape_key(key)}={self.escape_key(tag_values[key])}'
|
|
89
|
+
for key in final_tag_keys
|
|
72
90
|
)
|
|
73
91
|
|
|
74
|
-
if self.point_settings.defaultTags:
|
|
75
|
-
default_tags = ",".join(
|
|
76
|
-
f'{self.escape_key(key)}={self.escape_key(value)}'
|
|
77
|
-
for key, value in self.point_settings.defaultTags.items()
|
|
78
|
-
)
|
|
79
|
-
# Ensure there's a comma between existing tags and default tags if both are present
|
|
80
|
-
if tags and default_tags:
|
|
81
|
-
tags += ","
|
|
82
|
-
tags += default_tags
|
|
83
|
-
|
|
84
92
|
# add escape symbols for special characters to tags
|
|
85
93
|
|
|
86
94
|
fields = ",".join(
|
|
87
|
-
f"{col}=\"{self.escape_value(row[self.column_indices[col]])}\"" if isinstance(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
95
|
+
f"{self.escape_key(col)}=\"{self.escape_value(row[self.column_indices[col]])}\"" if isinstance(
|
|
96
|
+
row[self.column_indices[col]],
|
|
97
|
+
str)
|
|
98
|
+
else f"{self.escape_key(col)}={str(row[self.column_indices[col]]).lower()}" if isinstance(
|
|
99
|
+
row[self.column_indices[col]],
|
|
100
|
+
bool) # Check for bool first
|
|
101
|
+
else f"{self.escape_key(col)}={row[self.column_indices[col]]}i" if isinstance(row[self.column_indices[col]],
|
|
102
|
+
int)
|
|
103
|
+
else f"{self.escape_key(col)}={row[self.column_indices[col]]}"
|
|
93
104
|
for col in self.column_indices
|
|
94
105
|
if col not in self.tag_columns + [self.timestamp_column] and
|
|
95
106
|
row[self.column_indices[col]] is not None and row[self.column_indices[col]] != ""
|