influxdb3-python 0.7.0__tar.gz → 0.9.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.9.0/Examples/__init__.py +1 -0
  2. influxdb3_python-0.9.0/Examples/batching_example.py +114 -0
  3. influxdb3_python-0.9.0/Examples/cloud_dedicated_query.py +16 -0
  4. influxdb3_python-0.9.0/Examples/cloud_dedicated_write.py +49 -0
  5. influxdb3_python-0.9.0/Examples/config.py +13 -0
  6. influxdb3_python-0.9.0/Examples/flight_options_example.py +23 -0
  7. influxdb3_python-0.9.0/Examples/handle_http_error.py +43 -0
  8. influxdb3_python-0.9.0/Examples/pandas_write.py +35 -0
  9. influxdb3_python-0.9.0/Examples/query_type.py +55 -0
  10. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/PKG-INFO +2 -2
  11. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/README.md +1 -1
  12. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb3_python.egg-info/PKG-INFO +2 -2
  13. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb3_python.egg-info/SOURCES.txt +10 -1
  14. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb3_python.egg-info/top_level.txt +1 -0
  15. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/__init__.py +22 -1
  16. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/version.py +1 -1
  17. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/_base.py +5 -4
  18. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/exceptions.py +15 -1
  19. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/influxdb_client.py +1 -0
  20. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/write_api.py +1 -1
  21. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/setup.py +1 -0
  22. influxdb3_python-0.9.0/tests/test_api_client.py +141 -0
  23. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/tests/test_influxdb_client_3.py +21 -0
  24. influxdb3_python-0.9.0/tests/test_influxdb_client_3_integration.py +74 -0
  25. influxdb3_python-0.7.0/tests/test_polars_dataframe_serializer.py → influxdb3_python-0.9.0/tests/test_polars.py +34 -2
  26. influxdb3_python-0.7.0/tests/test_api_client.py +0 -71
  27. influxdb3_python-0.7.0/tests/test_influxdb_client_3_integration.py +0 -45
  28. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/LICENSE +0 -0
  29. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb3_python.egg-info/dependency_links.txt +0 -0
  30. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb3_python.egg-info/requires.txt +0 -0
  31. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/py.typed +0 -0
  32. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/query/__init__.py +0 -0
  33. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/query/query_api.py +0 -0
  34. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/read_file.py +0 -0
  35. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/__init__.py +0 -0
  36. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/_sync/__init__.py +0 -0
  37. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/_sync/api_client.py +0 -0
  38. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/_sync/rest.py +0 -0
  39. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/__init__.py +0 -0
  40. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/logging_handler.py +0 -0
  41. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/util/__init__.py +0 -0
  42. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/util/date_utils.py +0 -0
  43. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/util/date_utils_pandas.py +0 -0
  44. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/util/helpers.py +0 -0
  45. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/util/multiprocessing_helper.py +0 -0
  46. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/warnings.py +0 -0
  47. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/write/__init__.py +0 -0
  48. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/write/dataframe_serializer.py +0 -0
  49. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/write/point.py +0 -0
  50. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/write/polars_dataframe_serializer.py +0 -0
  51. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/client/write/retry.py +0 -0
  52. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/configuration.py +0 -0
  53. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/domain/__init__.py +0 -0
  54. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/domain/write_precision.py +0 -0
  55. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/extras.py +0 -0
  56. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/rest.py +0 -0
  57. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/service/__init__.py +0 -0
  58. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/service/_base_service.py +0 -0
  59. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/service/signin_service.py +0 -0
  60. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/service/signout_service.py +0 -0
  61. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/influxdb_client_3/write_client/service/write_service.py +0 -0
  62. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/setup.cfg +0 -0
  63. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/tests/test_dataframe_serializer.py +0 -0
  64. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/tests/test_date_helper.py +0 -0
  65. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/tests/test_deep_merge.py +0 -0
  66. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/tests/test_merge_options.py +0 -0
  67. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/tests/test_point.py +0 -0
  68. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/tests/test_query.py +0 -0
  69. {influxdb3_python-0.7.0 → influxdb3_python-0.9.0}/tests/test_write_file.py +0 -0
@@ -0,0 +1 @@
1
+ # used mainly to resolve local utility helpers like config.py
@@ -0,0 +1,114 @@
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)
@@ -0,0 +1,16 @@
1
+ from config import Config
2
+ import influxdb_client_3 as InfluxDBClient3
3
+
4
+ config = Config()
5
+
6
+ client = InfluxDBClient3.InfluxDBClient3(
7
+ token=config.token,
8
+ host=config.host,
9
+ org=config.org,
10
+ database=config.database)
11
+
12
+ table = client.query(
13
+ query="SELECT * FROM flight WHERE time > now() - 4h",
14
+ language="influxql")
15
+
16
+ print(table.to_pandas())
@@ -0,0 +1,49 @@
1
+ from config import Config
2
+ import influxdb_client_3 as InfluxDBClient3
3
+ from influxdb_client_3 import WriteOptions
4
+ import pandas as pd
5
+ import numpy as np
6
+
7
+ config = Config()
8
+
9
+ client = InfluxDBClient3.InfluxDBClient3(
10
+ token=config.token,
11
+ host=config.host,
12
+ org=config.org,
13
+ database=config.database,
14
+ write_options=WriteOptions(
15
+ batch_size=500,
16
+ flush_interval=10_000,
17
+ jitter_interval=2_000,
18
+ retry_interval=5_000,
19
+ max_retries=5,
20
+ max_retry_delay=30_000,
21
+ max_close_wait=300_000,
22
+ exponential_base=2,
23
+ write_type='batching'))
24
+
25
+
26
+ # Create a dataframe
27
+ df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
28
+
29
+
30
+ # Create a range of datetime values
31
+ dates = pd.date_range(start='2024-09-08', end='2024-09-09', freq='5min')
32
+
33
+ # Create a DataFrame with random data and datetime index
34
+ df = pd.DataFrame(
35
+ np.random.randn(
36
+ len(dates),
37
+ 3),
38
+ index=dates,
39
+ columns=[
40
+ 'Column 1',
41
+ 'Column 2',
42
+ 'Column 3'])
43
+ df['tagkey'] = 'Hello World'
44
+
45
+ print(df)
46
+
47
+ # Write the DataFrame to InfluxDB
48
+ client.write(df, data_frame_measurement_name='table',
49
+ data_frame_tag_columns=['tagkey'])
@@ -0,0 +1,13 @@
1
+ import os
2
+ import json
3
+
4
+
5
+ class Config:
6
+ def __init__(self):
7
+ self.host = os.getenv('INFLUXDB_HOST') or 'https://us-east-1-1.aws.cloud2.influxdata.com/'
8
+ self.token = os.getenv('INFLUXDB_TOKEN') or 'my-token'
9
+ self.org = os.getenv('INFLUXDB_ORG') or 'my-org'
10
+ self.database = os.getenv('INFLUXDB_DATABASE') or 'my-db'
11
+
12
+ def __str__(self):
13
+ return json.dumps(self.__dict__)
@@ -0,0 +1,23 @@
1
+ import influxdb_client_3 as InfluxDBClient3
2
+ from influxdb_client_3 import flight_client_options
3
+
4
+
5
+ with open("./cert.pem", 'rb') as f:
6
+ cert = f.read()
7
+ print(cert)
8
+
9
+
10
+ client = InfluxDBClient3.InfluxDBClient3(
11
+ token="",
12
+ host="b0c7cce5-8dbc-428e-98c6-7f996fb96467.a.influxdb.io",
13
+ org="6a841c0c08328fb1",
14
+ database="flightdemo",
15
+ flight_client_options=flight_client_options(
16
+ tls_root_certs=cert))
17
+
18
+
19
+ table = client.query(
20
+ query="SELECT * FROM flight WHERE time > now() - 4h",
21
+ language="influxql")
22
+
23
+ print(table.to_pandas())
@@ -0,0 +1,43 @@
1
+ """
2
+ Demonstrates handling response error headers on error.
3
+ """
4
+ import logging
5
+ from config import Config
6
+
7
+ import influxdb_client_3 as InfluxDBClient3
8
+
9
+
10
+ def main() -> None:
11
+ """
12
+ Main function
13
+ :return:
14
+ """
15
+ config = Config()
16
+ logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
17
+
18
+ client = InfluxDBClient3.InfluxDBClient3(
19
+ host=config.host,
20
+ token=config.token,
21
+ org=config.org,
22
+ database=config.database
23
+ )
24
+
25
+ # write with empty field results in HTTP 400 error
26
+ # Other cases might be HTTP 503 or HTTP 429 too many requests
27
+ lp = 'drone,location=harfa,id=A16E22 speed=18.7,alt=97.6,shutter='
28
+
29
+ try:
30
+ client.write(lp)
31
+ except InfluxDBClient3.InfluxDBError as idberr:
32
+ logging.log(logging.ERROR, 'WRITE ERROR: %s (%s)',
33
+ idberr.response.status,
34
+ idberr.message)
35
+ headers_string = 'Response Headers:\n'
36
+ headers = idberr.getheaders()
37
+ for h in headers:
38
+ headers_string += f' {h}: {headers[h]}\n'
39
+ logging.log(logging.INFO, headers_string)
40
+
41
+
42
+ if __name__ == "__main__":
43
+ main()
@@ -0,0 +1,35 @@
1
+ import influxdb_client_3 as InfluxDBClient3
2
+ import pandas as pd
3
+ import numpy as np
4
+
5
+ client = InfluxDBClient3.InfluxDBClient3(
6
+ token="",
7
+ host="eu-central-1-1.aws.cloud2.influxdata.com",
8
+ org="",
9
+ database="")
10
+
11
+
12
+ # Create a dataframe
13
+ df = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6]})
14
+
15
+
16
+ # Create a range of datetime values
17
+ dates = pd.date_range(start='2023-03-01', end='2023-03-29', freq='5min')
18
+
19
+ # Create a DataFrame with random data and datetime index
20
+ df = pd.DataFrame(
21
+ np.random.randn(
22
+ len(dates),
23
+ 3),
24
+ index=dates,
25
+ columns=[
26
+ 'Column 1',
27
+ 'Column 2',
28
+ 'Column 3'])
29
+ df['tagkey'] = 'Hello World'
30
+
31
+ print(df)
32
+
33
+ # Write the DataFrame to InfluxDB
34
+ client.write(df, data_frame_measurement_name='table',
35
+ data_frame_tag_columns=['tagkey'])
@@ -0,0 +1,55 @@
1
+ import influxdb_client_3 as InfluxDBClient3
2
+
3
+ client = InfluxDBClient3.InfluxDBClient3(
4
+ token="",
5
+ host="eu-central-1-1.aws.cloud2.influxdata.com",
6
+ org="6a841c0c08328fb1",
7
+ database="factory")
8
+
9
+
10
+ # Chunk mode provides a FlightReader object that can be used to read chunks of data.
11
+ reader = client.query(
12
+ query="SELECT * FROM machine_data WHERE time > now() - 2h",
13
+ language="influxql", mode="chunk")
14
+
15
+ try:
16
+ while True:
17
+ batch, buff = reader.read_chunk()
18
+ print("batch:")
19
+ print(batch.to_pandas())
20
+ except StopIteration:
21
+ print("No more chunks to read")
22
+
23
+
24
+ # Pandas mode provides a Pandas DataFrame object.
25
+ df = client.query(
26
+ query="SELECT * FROM machine_data WHERE time > now() - 2h",
27
+ language="influxql", mode="pandas")
28
+
29
+ print("pandas:")
30
+ print(df)
31
+
32
+ # All mode provides an Arrow Table object.
33
+ table = client.query(
34
+ query="SELECT * FROM machine_data WHERE time > now() - 2h",
35
+ language="influxql", mode="all")
36
+
37
+ print("table:")
38
+ print(table)
39
+
40
+ # Print the schema of the table
41
+ table = client.query(
42
+ query="SELECT * FROM machine_data WHERE time > now() - 2h",
43
+ language="influxql", mode="schema")
44
+
45
+ print("schema:")
46
+ print(table)
47
+
48
+ # Convert this reader into a regular RecordBatchReader
49
+ reader = client.query(
50
+ query="SELECT * FROM machine_data WHERE time > now() - 2h",
51
+ language="influxql", mode="reader")
52
+
53
+ print("reader:")
54
+ for batch in reader:
55
+ print(batch.to_pandas())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: influxdb3-python
3
- Version: 0.7.0
3
+ Version: 0.9.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
@@ -93,7 +93,7 @@ from influxdb_client_3 import InfluxDBClient3, Point
93
93
  ## Initialization
94
94
  If you are using InfluxDB Cloud, then you should note that:
95
95
  1. You will need to supply your org id, this is not necessary for InfluxDB Dedicated.
96
- 2. Use a bucketname for the database argument.
96
+ 2. Use bucket name for the `database` argument.
97
97
 
98
98
  ```python
99
99
  client = InfluxDBClient3(token="your-token",
@@ -58,7 +58,7 @@ from influxdb_client_3 import InfluxDBClient3, Point
58
58
  ## Initialization
59
59
  If you are using InfluxDB Cloud, then you should note that:
60
60
  1. You will need to supply your org id, this is not necessary for InfluxDB Dedicated.
61
- 2. Use a bucketname for the database argument.
61
+ 2. Use bucket name for the `database` argument.
62
62
 
63
63
  ```python
64
64
  client = InfluxDBClient3(token="your-token",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: influxdb3-python
3
- Version: 0.7.0
3
+ Version: 0.9.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
@@ -93,7 +93,7 @@ from influxdb_client_3 import InfluxDBClient3, Point
93
93
  ## Initialization
94
94
  If you are using InfluxDB Cloud, then you should note that:
95
95
  1. You will need to supply your org id, this is not necessary for InfluxDB Dedicated.
96
- 2. Use a bucketname for the database argument.
96
+ 2. Use bucket name for the `database` argument.
97
97
 
98
98
  ```python
99
99
  client = InfluxDBClient3(token="your-token",
@@ -1,6 +1,15 @@
1
1
  LICENSE
2
2
  README.md
3
3
  setup.py
4
+ Examples/__init__.py
5
+ Examples/batching_example.py
6
+ Examples/cloud_dedicated_query.py
7
+ Examples/cloud_dedicated_write.py
8
+ Examples/config.py
9
+ Examples/flight_options_example.py
10
+ Examples/handle_http_error.py
11
+ Examples/pandas_write.py
12
+ Examples/query_type.py
4
13
  influxdb3_python.egg-info/PKG-INFO
5
14
  influxdb3_python.egg-info/SOURCES.txt
6
15
  influxdb3_python.egg-info/dependency_links.txt
@@ -51,6 +60,6 @@ tests/test_influxdb_client_3.py
51
60
  tests/test_influxdb_client_3_integration.py
52
61
  tests/test_merge_options.py
53
62
  tests/test_point.py
54
- tests/test_polars_dataframe_serializer.py
63
+ tests/test_polars.py
55
64
  tests/test_query.py
56
65
  tests/test_write_file.py
@@ -110,7 +110,28 @@ class InfluxDBClient3:
110
110
  :type write_client_options: callable
111
111
  :param flight_client_options: Function for providing additional arguments for the FlightClient.
112
112
  :type flight_client_options: callable
113
- :param kwargs: Additional arguments for the InfluxDB Client.
113
+ :key auth_scheme: token authentication scheme. Set to "Bearer" for Edge.
114
+ :key bool verify_ssl: Set this to false to skip verifying SSL certificate when calling API from https server.
115
+ :key str ssl_ca_cert: Set this to customize the certificate file to verify the peer.
116
+ :key str cert_file: Path to the certificate that will be used for mTLS authentication.
117
+ :key str cert_key_file: Path to the file contains private key for mTLS certificate.
118
+ :key str cert_key_password: String or function which returns password for decrypting the mTLS private key.
119
+ :key ssl.SSLContext ssl_context: Specify a custom Python SSL Context for the TLS/ mTLS handshake.
120
+ Be aware that only delivered certificate/ key files or an SSL Context are
121
+ possible.
122
+ :key str proxy: Set this to configure the http proxy to be used (ex. http://localhost:3128)
123
+ :key str proxy_headers: A dictionary containing headers that will be sent to the proxy. Could be used for proxy
124
+ authentication.
125
+ :key int connection_pool_maxsize: Number of connections to save that can be reused by urllib3.
126
+ Defaults to "multiprocessing.cpu_count() * 5".
127
+ :key urllib3.util.retry.Retry retries: Set the default retry strategy that is used for all HTTP requests
128
+ except batching writes. As a default there is no one retry strategy.
129
+ :key bool auth_basic: Set this to true to enable basic authentication when talking to a InfluxDB 1.8.x that
130
+ does not use auth-enabled but is protected by a reverse proxy with basic authentication.
131
+ (defaults to false, don't set to true when talking to InfluxDB 2)
132
+ :key str username: ``username`` to authenticate via username and password credentials to the InfluxDB 2.x
133
+ :key str password: ``password`` to authenticate via username and password credentials to the InfluxDB 2.x
134
+ :key list[str] profilers: list of enabled Flux profilers
114
135
  """
115
136
  self._org = org if org is not None else "default"
116
137
  self._database = database
@@ -1,4 +1,4 @@
1
1
  """Version of the Client that is used in User-Agent header."""
2
2
 
3
- VERSION = '0.7.0'
3
+ VERSION = '0.9.0'
4
4
  USER_AGENT = f'influxdb3-python/{VERSION}'
@@ -37,7 +37,6 @@ class _BaseClient(object):
37
37
  def __init__(self, url, token, debug=None, timeout=10_000, enable_gzip=False, org: str = None,
38
38
  default_tags: dict = None, http_client_logger: str = None, **kwargs) -> None:
39
39
  self.url = url
40
- self.token = token
41
40
  self.org = org
42
41
 
43
42
  self.default_tags = default_tags
@@ -70,9 +69,10 @@ class _BaseClient(object):
70
69
  self.auth_header_name = None
71
70
  self.auth_header_value = None
72
71
  # by token
73
- if self.token:
72
+ if token:
73
+ auth_scheme = kwargs.get('auth_scheme', "Token")
74
74
  self.auth_header_name = "Authorization"
75
- self.auth_header_value = "Token " + self.token
75
+ self.auth_header_value = f"{auth_scheme} {token}"
76
76
  # by HTTP basic
77
77
  auth_basic = kwargs.get('auth_basic', False)
78
78
  if auth_basic:
@@ -248,7 +248,8 @@ class _BaseWriteApi(object):
248
248
  self._serialize(Point.from_dict(record, write_precision=write_precision, **kwargs),
249
249
  write_precision, payload, **kwargs)
250
250
  elif 'polars' in str(type(record)):
251
- from influxdb_client_3.write_client.client.write.dataframe_serializer import PolarsDataframeSerializer
251
+ from influxdb_client_3.write_client.client.write.polars_dataframe_serializer import \
252
+ PolarsDataframeSerializer
252
253
  serializer = PolarsDataframeSerializer(record, self._point_settings, write_precision, **kwargs)
253
254
  self._serialize(serializer.serialize(), write_precision, payload, **kwargs)
254
255
 
@@ -26,8 +26,18 @@ class InfluxDBError(Exception):
26
26
  # Body
27
27
  if response.data:
28
28
  import json
29
+
30
+ def get(d, key):
31
+ if not key or d is None:
32
+ return d
33
+ return get(d.get(key[0]), key[1:])
29
34
  try:
30
- return json.loads(response.data)["message"]
35
+ node = json.loads(response.data)
36
+ for key in [['message'], ['data', 'error_message'], ['error']]:
37
+ value = get(node, key)
38
+ if value is not None:
39
+ return value
40
+ return response.data
31
41
  except Exception as e:
32
42
  logging.debug(f"Cannot parse error response to JSON: {response.data}, {e}")
33
43
  return response.data
@@ -40,3 +50,7 @@ class InfluxDBError(Exception):
40
50
 
41
51
  # Http Status
42
52
  return response.reason
53
+
54
+ def getheaders(self):
55
+ """Helper method to make response headers more accessible."""
56
+ return self.response.getheaders()
@@ -27,6 +27,7 @@ class InfluxDBClient(_BaseClient):
27
27
  :param enable_gzip: Enable Gzip compression for http requests. Currently, only the "Write" and "Query" endpoints
28
28
  supports the Gzip compression.
29
29
  :param org: organization name (used as a default in Query, Write and Delete API)
30
+ :key auth_scheme: token authentication scheme. Set to "Bearer" for Edge.
30
31
  :key bool verify_ssl: Set this to false to skip verifying SSL certificate when calling API from https server.
31
32
  :key str ssl_ca_cert: Set this to customize the certificate file to verify the peer.
32
33
  :key str cert_file: Path to the certificate that will be used for mTLS authentication.
@@ -566,7 +566,7 @@ You can use native asynchronous version of the client:
566
566
 
567
567
  def _on_complete(self):
568
568
  self._disposable.dispose()
569
- logger.info("the batching processor was disposed")
569
+ logger.debug("the batching processor was disposed")
570
570
 
571
571
  def __getstate__(self):
572
572
  """Return a dict of attributes that you want to pickle."""
@@ -47,6 +47,7 @@ setup(
47
47
  author_email='contact@influxdata.com',
48
48
  url='https://github.com/InfluxCommunity/influxdb3-python',
49
49
  packages=find_packages(exclude=['tests', 'tests.*', 'examples', 'examples.*']),
50
+ package_data={'influxdb_client_3': ['py.typed']},
50
51
  extras_require={
51
52
  'pandas': ['pandas'],
52
53
  'polars': ['polars'],
@@ -0,0 +1,141 @@
1
+ import json
2
+ import unittest
3
+ import uuid
4
+ from unittest import mock
5
+ from urllib3 import response
6
+
7
+ from influxdb_client_3.write_client._sync.api_client import ApiClient
8
+ from influxdb_client_3.write_client.configuration import Configuration
9
+ from influxdb_client_3.write_client.client.exceptions import InfluxDBError
10
+ from influxdb_client_3.write_client.service import WriteService
11
+ from influxdb_client_3.version import VERSION
12
+
13
+ _package = "influxdb3-python"
14
+ _sentHeaders = {}
15
+
16
+
17
+ def mock_rest_request(method,
18
+ url,
19
+ query_params=None,
20
+ headers=None,
21
+ body=None,
22
+ post_params=None,
23
+ _preload_content=True,
24
+ _request_timeout=None,
25
+ **urlopen_kw):
26
+ class MockResponse:
27
+ def __init__(self, data, status_code):
28
+ self.data = data
29
+ self.status_code = status_code
30
+
31
+ def data(self):
32
+ return self.data
33
+
34
+ global _sentHeaders
35
+ _sentHeaders = headers
36
+
37
+ return MockResponse(None, 200)
38
+
39
+
40
+ class ApiClientTests(unittest.TestCase):
41
+
42
+ def test_default_headers(self):
43
+ global _package
44
+ conf = Configuration()
45
+ client = ApiClient(conf,
46
+ header_name="Authorization",
47
+ header_value="Bearer TEST_TOKEN")
48
+ self.assertIsNotNone(client.default_headers["User-Agent"])
49
+ self.assertIsNotNone(client.default_headers["Authorization"])
50
+ self.assertEqual(f"{_package}/{VERSION}", client.default_headers["User-Agent"])
51
+ self.assertEqual("Bearer TEST_TOKEN", client.default_headers["Authorization"])
52
+
53
+ @mock.patch("influxdb_client_3.write_client._sync.rest.RESTClientObject.request",
54
+ side_effect=mock_rest_request)
55
+ def test_call_api(self, mock_post):
56
+ global _package
57
+ global _sentHeaders
58
+ _sentHeaders = {}
59
+
60
+ conf = Configuration()
61
+ client = ApiClient(conf,
62
+ header_name="Authorization",
63
+ header_value="Bearer TEST_TOKEN")
64
+ service = WriteService(client)
65
+ service.post_write("TEST_ORG", "TEST_BUCKET", "data,foo=bar val=3.14")
66
+ self.assertEqual(4, len(_sentHeaders.keys()))
67
+ self.assertIsNotNone(_sentHeaders["Accept"])
68
+ self.assertEqual("application/json", _sentHeaders["Accept"])
69
+ self.assertIsNotNone(_sentHeaders["Content-Type"])
70
+ self.assertEqual("text/plain", _sentHeaders["Content-Type"])
71
+ self.assertIsNotNone(_sentHeaders["Authorization"])
72
+ self.assertEqual("Bearer TEST_TOKEN", _sentHeaders["Authorization"])
73
+ self.assertIsNotNone(_sentHeaders["User-Agent"])
74
+ self.assertEqual(f"{_package}/{VERSION}", _sentHeaders["User-Agent"])
75
+
76
+ def _test_api_error(self, body):
77
+ conf = Configuration()
78
+ client = ApiClient(conf)
79
+ client.rest_client.pool_manager.request \
80
+ = mock.Mock(return_value=response.HTTPResponse(status=400,
81
+ reason='Bad Request',
82
+ body=body.encode()))
83
+ service = WriteService(client)
84
+ service.post_write("TEST_ORG", "TEST_BUCKET", "data,foo=bar val=3.14")
85
+
86
+ def test_api_error_cloud(self):
87
+ response_body = '{"message": "parsing failed for write_lp endpoint"}'
88
+ with self.assertRaises(InfluxDBError) as err:
89
+ self._test_api_error(response_body)
90
+ self.assertEqual('parsing failed for write_lp endpoint', err.exception.message)
91
+
92
+ def test_api_error_oss_without_detail(self):
93
+ response_body = '{"error": "parsing failed for write_lp endpoint"}'
94
+ with self.assertRaises(InfluxDBError) as err:
95
+ self._test_api_error(response_body)
96
+ self.assertEqual('parsing failed for write_lp endpoint', err.exception.message)
97
+
98
+ def test_api_error_oss_with_detail(self):
99
+ response_body = ('{"error":"parsing failed for write_lp endpoint","data":{"error_message":"invalid field value '
100
+ 'in line protocol for field \'val\' on line 1"}}')
101
+ with self.assertRaises(InfluxDBError) as err:
102
+ self._test_api_error(response_body)
103
+ self.assertEqual('invalid field value in line protocol for field \'val\' on line 1', err.exception.message)
104
+
105
+ def test_api_error_unknown(self):
106
+ response_body = '{"detail":"no info"}'
107
+ with self.assertRaises(InfluxDBError) as err:
108
+ self._test_api_error(response_body)
109
+ self.assertEqual(response_body, err.exception.message)
110
+
111
+ def test_api_error_headers(self):
112
+ body = '{"error": "test error"}'
113
+ body_dic = json.loads(body)
114
+ conf = Configuration()
115
+ local_client = ApiClient(conf)
116
+ traceid = "123456789ABCDEF0"
117
+ requestid = uuid.uuid4().__str__()
118
+
119
+ local_client.rest_client.pool_manager.request = mock.Mock(
120
+ return_value=response.HTTPResponse(
121
+ status=400,
122
+ reason='Bad Request',
123
+ headers={
124
+ 'Trace-Id': traceid,
125
+ 'Trace-Sampled': 'false',
126
+ 'X-Influxdb-Request-Id': requestid,
127
+ 'X-Influxdb-Build': 'Mock'
128
+ },
129
+ body=body.encode()
130
+ )
131
+ )
132
+ with self.assertRaises(InfluxDBError) as err:
133
+ service = WriteService(local_client)
134
+ service.post_write("TEST_ORG", "TEST_BUCKET", "data,foo=bar val=3.14")
135
+ self.assertEqual(body_dic['error'], err.exception.message)
136
+ headers = err.exception.getheaders()
137
+ self.assertEqual(4, len(headers))
138
+ self.assertEqual(headers['Trace-Id'], traceid)
139
+ self.assertEqual(headers['Trace-Sampled'], 'false')
140
+ self.assertEqual(headers['X-Influxdb-Request-Id'], requestid)
141
+ self.assertEqual(headers['X-Influxdb-Build'], 'Mock')
@@ -27,6 +27,27 @@ class TestInfluxDBClient3(unittest.TestCase):
27
27
  self.assertEqual(self.client._write_api, self.mock_write_api.return_value)
28
28
  self.assertEqual(self.client._query_api, self.mock_query_api.return_value)
29
29
 
30
+ # test default token auth_scheme
31
+ def test_token_auth_scheme_default(self):
32
+ client = InfluxDBClient3(
33
+ host="localhost",
34
+ org="my_org",
35
+ database="my_db",
36
+ token="my_token",
37
+ )
38
+ self.assertEqual(client._client.auth_header_value, "Token my_token")
39
+
40
+ # test explicit token auth_scheme
41
+ def test_token_auth_scheme_explicit(self):
42
+ client = InfluxDBClient3(
43
+ host="localhost",
44
+ org="my_org",
45
+ database="my_db",
46
+ token="my_token",
47
+ auth_scheme="my_scheme"
48
+ )
49
+ self.assertEqual(client._client.auth_header_value, "my_scheme my_token")
50
+
30
51
 
31
52
  if __name__ == '__main__':
32
53
  unittest.main()
@@ -0,0 +1,74 @@
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')
@@ -1,7 +1,9 @@
1
- import unittest
2
1
  import importlib.util
2
+ import unittest
3
+ from unittest.mock import Mock
3
4
 
4
- from influxdb_client_3 import PointSettings
5
+ from influxdb_client_3 import PointSettings, InfluxDBClient3
6
+ from influxdb_client_3.write_client import WriteService
5
7
  from influxdb_client_3.write_client.client.write.polars_dataframe_serializer import polars_data_frame_to_list_of_points
6
8
 
7
9
 
@@ -29,3 +31,33 @@ class TestPolarsDataFrameSerializer(unittest.TestCase):
29
31
  'iot-devices,building=5a name="iot-devices",temperature=72.2 1664798460000000000'
30
32
  ]
31
33
  self.assertEqual(expected, actual)
34
+
35
+
36
+ @unittest.skipIf(importlib.util.find_spec("polars") is None, 'Polars package not installed')
37
+ class TestWritePolars(unittest.TestCase):
38
+ def setUp(self):
39
+ self.client = InfluxDBClient3(
40
+ host="localhost",
41
+ org="my_org",
42
+ database="my_db",
43
+ token="my_token"
44
+ )
45
+
46
+ def test_write_polars(self):
47
+ import polars as pl
48
+ df = pl.DataFrame({
49
+ "time": pl.Series(["2024-08-01 00:00:00", "2024-08-01 01:00:00"]).str.to_datetime(time_unit='ns'),
50
+ "temperature": [22.4, 21.8],
51
+ })
52
+ self.client._write_api._write_service = Mock(spec=WriteService)
53
+
54
+ self.client.write(
55
+ database="database",
56
+ record=df,
57
+ data_frame_measurement_name="measurement",
58
+ data_frame_timestamp_column="time",
59
+ )
60
+
61
+ actual = self.client._write_api._write_service.post_write.call_args[1]['body']
62
+ self.assertEqual(b'measurement temperature=22.4 1722470400000000000\n'
63
+ b'measurement temperature=21.8 1722474000000000000', actual)
@@ -1,71 +0,0 @@
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"])
@@ -1,45 +0,0 @@
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])