influxdata-plugin-utils 0.1.0__tar.gz → 0.2.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.
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/CHANGELOG.md +20 -2
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/PKG-INFO +26 -3
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/README.md +25 -2
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/src/influxdata_plugin_utils/__init__.py +1 -1
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/src/influxdata_plugin_utils/introspection.py +63 -11
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/src/influxdata_plugin_utils/parsing.py +3 -1
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/src/influxdata_plugin_utils/write.py +25 -4
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/.gitignore +0 -0
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/LICENSE-APACHE +0 -0
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/LICENSE-MIT +0 -0
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/pyproject.toml +0 -0
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/src/influxdata_plugin_utils/cache.py +0 -0
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/src/influxdata_plugin_utils/config.py +0 -0
- {influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/src/influxdata_plugin_utils/py.typed +0 -0
|
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-07-12
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `write.write_data` — optional `database` parameter for writing to another
|
|
15
|
+
database.
|
|
16
|
+
- `introspection` — optional `database` parameter for schema helpers and
|
|
17
|
+
`query_window`.
|
|
18
|
+
- `parsing.parse_timedelta` — `ms` (milliseconds) and `us` (microseconds)
|
|
19
|
+
duration units.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
|
|
23
|
+
- `write.write_data` — `no_sync` now defaults to `None`: writes go through
|
|
24
|
+
`write` / `write_to_db` (available on all InfluxDB 3 versions); passing a
|
|
25
|
+
boolean switches to `write_sync` / `write_sync_to_db` (InfluxDB 3.8+).
|
|
26
|
+
|
|
10
27
|
## [0.1.0] - 2026-07-08
|
|
11
28
|
|
|
12
29
|
### Added
|
|
@@ -22,5 +39,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
22
39
|
- `write` — `build_line`, `build_line_typed`, `add_field_with_type`,
|
|
23
40
|
`write_data` (batching + retry), `BatchLines`.
|
|
24
41
|
|
|
25
|
-
[Unreleased]: https://github.com/influxdata/influxdb3_plugins/compare/utils-v0.
|
|
26
|
-
[0.
|
|
42
|
+
[Unreleased]: https://github.com/influxdata/influxdb3_plugins/compare/utils-v0.2.0...HEAD
|
|
43
|
+
[0.2.0]: https://github.com/influxdata/influxdb3_plugins/compare/utils-v0.1.0...utils-v0.2.0
|
|
44
|
+
[0.1.0]: https://github.com/influxdata/influxdb3_plugins/releases/tag/utils-v0.1.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: influxdata-plugin-utils
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Shared helpers for InfluxDB 3 plugins.
|
|
5
5
|
Project-URL: Homepage, https://github.com/influxdata/influxdb3_plugins
|
|
6
6
|
Project-URL: Repository, https://github.com/influxdata/influxdb3_plugins
|
|
@@ -36,7 +36,7 @@ pip install -e influxdata-plugin-utils
|
|
|
36
36
|
| Module | What it provides |
|
|
37
37
|
|-----------------|-----------------------------------------------------------------------------------------------------------------------------|
|
|
38
38
|
| `config` | `load_plugin_config(args, validators)` (dynaconf-backed), `resolve_plugin_dir()`, `resolve_path()`, re-exported `Validator` |
|
|
39
|
-
| `introspection` | `get_table_names()`, `get_tag_names()`, `get_field_names()`, `query_window()`
|
|
39
|
+
| `introspection` | `get_table_names()`, `get_tag_names()`, `get_field_names()`, `query_window()` with optional `database=` |
|
|
40
40
|
| `parsing` | `parse_timedelta()`, `parse_timestamp_ns()`, `parse_int()`, `parse_bool()`, `parse_delimited_list()`, `parse_key_value()` |
|
|
41
41
|
| `cache` | `cached(influxdb3_local, key, producer, ttl_seconds=3600)` |
|
|
42
42
|
| `write` | `build_line()`, `build_line_typed()`, `add_field_with_type()`, `write_data()`, `BatchLines` |
|
|
@@ -82,9 +82,32 @@ lines = [
|
|
|
82
82
|
]
|
|
83
83
|
write_data(influxdb3_local, lines) # batched + retried by default
|
|
84
84
|
# write_data(influxdb3_local, lines, batch=False, retries=0) # opt out
|
|
85
|
+
# write_data(influxdb3_local, lines, database="other_db") # another database
|
|
86
|
+
# write_data(influxdb3_local, lines, no_sync=True) # write_sync API (3.8+)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Cross-database queries
|
|
90
|
+
|
|
91
|
+
On InfluxDB versions that support processing-engine cross-database queries,
|
|
92
|
+
the introspection helpers accept `database=` and pass it through to
|
|
93
|
+
`influxdb3_local.query`.
|
|
94
|
+
Cached schema results are separated per database.
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from influxdata_plugin_utils.introspection import get_field_names, query_window
|
|
98
|
+
|
|
99
|
+
fields = get_field_names(influxdb3_local, "cpu", database="source_db")
|
|
100
|
+
rows = query_window(
|
|
101
|
+
influxdb3_local,
|
|
102
|
+
"cpu",
|
|
103
|
+
start=start,
|
|
104
|
+
end=end,
|
|
105
|
+
columns=fields,
|
|
106
|
+
database="source_db",
|
|
107
|
+
)
|
|
85
108
|
```
|
|
86
109
|
|
|
87
110
|
## License
|
|
88
111
|
|
|
89
112
|
Licensed under either of [Apache License 2.0](LICENSE-APACHE) or
|
|
90
|
-
[MIT license](LICENSE-MIT) at your option.
|
|
113
|
+
[MIT license](LICENSE-MIT) at your option.
|
|
@@ -19,7 +19,7 @@ pip install -e influxdata-plugin-utils
|
|
|
19
19
|
| Module | What it provides |
|
|
20
20
|
|-----------------|-----------------------------------------------------------------------------------------------------------------------------|
|
|
21
21
|
| `config` | `load_plugin_config(args, validators)` (dynaconf-backed), `resolve_plugin_dir()`, `resolve_path()`, re-exported `Validator` |
|
|
22
|
-
| `introspection` | `get_table_names()`, `get_tag_names()`, `get_field_names()`, `query_window()`
|
|
22
|
+
| `introspection` | `get_table_names()`, `get_tag_names()`, `get_field_names()`, `query_window()` with optional `database=` |
|
|
23
23
|
| `parsing` | `parse_timedelta()`, `parse_timestamp_ns()`, `parse_int()`, `parse_bool()`, `parse_delimited_list()`, `parse_key_value()` |
|
|
24
24
|
| `cache` | `cached(influxdb3_local, key, producer, ttl_seconds=3600)` |
|
|
25
25
|
| `write` | `build_line()`, `build_line_typed()`, `add_field_with_type()`, `write_data()`, `BatchLines` |
|
|
@@ -65,9 +65,32 @@ lines = [
|
|
|
65
65
|
]
|
|
66
66
|
write_data(influxdb3_local, lines) # batched + retried by default
|
|
67
67
|
# write_data(influxdb3_local, lines, batch=False, retries=0) # opt out
|
|
68
|
+
# write_data(influxdb3_local, lines, database="other_db") # another database
|
|
69
|
+
# write_data(influxdb3_local, lines, no_sync=True) # write_sync API (3.8+)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Cross-database queries
|
|
73
|
+
|
|
74
|
+
On InfluxDB versions that support processing-engine cross-database queries,
|
|
75
|
+
the introspection helpers accept `database=` and pass it through to
|
|
76
|
+
`influxdb3_local.query`.
|
|
77
|
+
Cached schema results are separated per database.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from influxdata_plugin_utils.introspection import get_field_names, query_window
|
|
81
|
+
|
|
82
|
+
fields = get_field_names(influxdb3_local, "cpu", database="source_db")
|
|
83
|
+
rows = query_window(
|
|
84
|
+
influxdb3_local,
|
|
85
|
+
"cpu",
|
|
86
|
+
start=start,
|
|
87
|
+
end=end,
|
|
88
|
+
columns=fields,
|
|
89
|
+
database="source_db",
|
|
90
|
+
)
|
|
68
91
|
```
|
|
69
92
|
|
|
70
93
|
## License
|
|
71
94
|
|
|
72
95
|
Licensed under either of [Apache License 2.0](LICENSE-APACHE) or
|
|
73
|
-
[MIT license](LICENSE-MIT) at your option.
|
|
96
|
+
[MIT license](LICENSE-MIT) at your option.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Each helper takes ``influxdb3_local`` explicitly and may optionally use the
|
|
4
4
|
TTL cache. Queries mirror the patterns already used across the plugins.
|
|
5
|
+
Helpers accept an optional ``database`` argument for engines that support
|
|
6
|
+
cross-database plugin queries.
|
|
5
7
|
"""
|
|
6
8
|
|
|
7
9
|
from .cache import cached
|
|
@@ -22,13 +24,40 @@ def _quote_identifier(identifier: str) -> str:
|
|
|
22
24
|
return '"' + identifier.replace('"', '""') + '"'
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
def _query(
|
|
28
|
+
influxdb3_local,
|
|
29
|
+
query: str,
|
|
30
|
+
args: dict | None = None,
|
|
31
|
+
database: str | None = None,
|
|
32
|
+
):
|
|
33
|
+
"""Run a plugin query, preserving the old call shape when no database is set."""
|
|
34
|
+
if database is None:
|
|
35
|
+
if args is None:
|
|
36
|
+
return influxdb3_local.query(query)
|
|
37
|
+
return influxdb3_local.query(query, args)
|
|
38
|
+
if args is None:
|
|
39
|
+
return influxdb3_local.query(query, database=database)
|
|
40
|
+
return influxdb3_local.query(query, args, database=database)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _cache_key(base: str, database: str | None) -> str:
|
|
44
|
+
"""Return a cache key, keeping existing default-database keys unchanged."""
|
|
45
|
+
if database is None:
|
|
46
|
+
return base
|
|
47
|
+
return f"{base}:database:{database}"
|
|
48
|
+
|
|
49
|
+
|
|
25
50
|
def get_table_names(
|
|
26
|
-
influxdb3_local,
|
|
51
|
+
influxdb3_local,
|
|
52
|
+
*,
|
|
53
|
+
database: str | None = None,
|
|
54
|
+
use_cache: bool = True,
|
|
55
|
+
ttl_seconds: int = 3600,
|
|
27
56
|
) -> list[str]:
|
|
28
57
|
"""Return base table names via ``SHOW TABLES``."""
|
|
29
58
|
|
|
30
59
|
def producer() -> list[str]:
|
|
31
|
-
rows = influxdb3_local
|
|
60
|
+
rows = _query(influxdb3_local, "SHOW TABLES", database=database)
|
|
32
61
|
return [
|
|
33
62
|
row["table_name"]
|
|
34
63
|
for row in rows
|
|
@@ -36,12 +65,22 @@ def get_table_names(
|
|
|
36
65
|
]
|
|
37
66
|
|
|
38
67
|
if use_cache:
|
|
39
|
-
return cached(
|
|
68
|
+
return cached(
|
|
69
|
+
influxdb3_local,
|
|
70
|
+
_cache_key("shared:tables", database),
|
|
71
|
+
producer,
|
|
72
|
+
ttl_seconds=ttl_seconds,
|
|
73
|
+
)
|
|
40
74
|
return producer()
|
|
41
75
|
|
|
42
76
|
|
|
43
77
|
def get_tag_names(
|
|
44
|
-
influxdb3_local,
|
|
78
|
+
influxdb3_local,
|
|
79
|
+
table: str,
|
|
80
|
+
*,
|
|
81
|
+
database: str | None = None,
|
|
82
|
+
use_cache: bool = True,
|
|
83
|
+
ttl_seconds: int = 3600,
|
|
45
84
|
) -> list[str]:
|
|
46
85
|
"""Return tag column names of a table (``Dictionary(Int32, Utf8)`` columns)."""
|
|
47
86
|
|
|
@@ -50,14 +89,20 @@ def get_tag_names(
|
|
|
50
89
|
"SELECT column_name FROM information_schema.columns "
|
|
51
90
|
"WHERE table_name = $table AND data_type = $data_type"
|
|
52
91
|
)
|
|
53
|
-
rows =
|
|
54
|
-
|
|
92
|
+
rows = _query(
|
|
93
|
+
influxdb3_local,
|
|
94
|
+
query,
|
|
95
|
+
{"table": table, "data_type": _TAG_DATA_TYPE},
|
|
96
|
+
database=database,
|
|
55
97
|
)
|
|
56
98
|
return [row["column_name"] for row in rows]
|
|
57
99
|
|
|
58
100
|
if use_cache:
|
|
59
101
|
return cached(
|
|
60
|
-
influxdb3_local,
|
|
102
|
+
influxdb3_local,
|
|
103
|
+
_cache_key(f"shared:tags:{table}", database),
|
|
104
|
+
producer,
|
|
105
|
+
ttl_seconds=ttl_seconds,
|
|
61
106
|
)
|
|
62
107
|
return producer()
|
|
63
108
|
|
|
@@ -67,6 +112,7 @@ def get_field_names(
|
|
|
67
112
|
table: str,
|
|
68
113
|
*,
|
|
69
114
|
numeric_only: bool = False,
|
|
115
|
+
database: str | None = None,
|
|
70
116
|
use_cache: bool = True,
|
|
71
117
|
ttl_seconds: int = 3600,
|
|
72
118
|
) -> list[str]:
|
|
@@ -80,7 +126,7 @@ def get_field_names(
|
|
|
80
126
|
"SELECT column_name, data_type FROM information_schema.columns "
|
|
81
127
|
"WHERE table_name = $table"
|
|
82
128
|
)
|
|
83
|
-
rows = influxdb3_local
|
|
129
|
+
rows = _query(influxdb3_local, query, {"table": table}, database=database)
|
|
84
130
|
names: list[str] = []
|
|
85
131
|
for row in rows:
|
|
86
132
|
name = row["column_name"]
|
|
@@ -93,7 +139,7 @@ def get_field_names(
|
|
|
93
139
|
return names
|
|
94
140
|
|
|
95
141
|
if use_cache:
|
|
96
|
-
key = f"shared:fields:{table}:{int(numeric_only)}"
|
|
142
|
+
key = _cache_key(f"shared:fields:{table}:{int(numeric_only)}", database)
|
|
97
143
|
return cached(influxdb3_local, key, producer, ttl_seconds=ttl_seconds)
|
|
98
144
|
return producer()
|
|
99
145
|
|
|
@@ -105,6 +151,7 @@ def query_window(
|
|
|
105
151
|
start,
|
|
106
152
|
end,
|
|
107
153
|
columns: list[str] | None = None,
|
|
154
|
+
database: str | None = None,
|
|
108
155
|
) -> list[dict]:
|
|
109
156
|
"""Run a basic ``time``-window query and return rows (``[]`` if none).
|
|
110
157
|
|
|
@@ -118,5 +165,10 @@ def query_window(
|
|
|
118
165
|
f"SELECT {selected} FROM {_quote_identifier(table)} "
|
|
119
166
|
"WHERE time >= $start AND time < $end ORDER BY time"
|
|
120
167
|
)
|
|
121
|
-
rows =
|
|
122
|
-
|
|
168
|
+
rows = _query(
|
|
169
|
+
influxdb3_local,
|
|
170
|
+
query,
|
|
171
|
+
{"start": start, "end": end},
|
|
172
|
+
database=database,
|
|
173
|
+
)
|
|
174
|
+
return rows or []
|
|
@@ -19,6 +19,8 @@ __all__ = [
|
|
|
19
19
|
|
|
20
20
|
_DURATION_RE = re.compile(r"^\s*(\d+)\s*([a-zA-Z]+)\s*$")
|
|
21
21
|
_DURATION_UNITS = {
|
|
22
|
+
"us": "microseconds",
|
|
23
|
+
"ms": "milliseconds",
|
|
22
24
|
"s": "seconds",
|
|
23
25
|
"min": "minutes",
|
|
24
26
|
"h": "hours",
|
|
@@ -33,7 +35,7 @@ _FALSE = {"false", "f", "0", "no", "off"}
|
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
def parse_timedelta(raw) -> timedelta:
|
|
36
|
-
"""Parse a duration like ``30s``, ``5min``, ``1h``, ``2d``, ``1w``."""
|
|
38
|
+
"""Parse a duration like ``500us``, ``100ms``, ``30s``, ``5min``, ``1h``, ``2d``, ``1w``."""
|
|
37
39
|
if isinstance(raw, timedelta):
|
|
38
40
|
return raw
|
|
39
41
|
match = _DURATION_RE.match(str(raw))
|
{influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/src/influxdata_plugin_utils/write.py
RENAMED
|
@@ -9,6 +9,7 @@ write helpers operate on already-built line objects and need no class.
|
|
|
9
9
|
import math
|
|
10
10
|
import random
|
|
11
11
|
import time
|
|
12
|
+
from functools import partial
|
|
12
13
|
|
|
13
14
|
from .parsing import parse_bool
|
|
14
15
|
|
|
@@ -160,7 +161,8 @@ def write_data(
|
|
|
160
161
|
batch: bool = True,
|
|
161
162
|
retries: int = 3,
|
|
162
163
|
base_delay: float = 1.0,
|
|
163
|
-
no_sync: bool =
|
|
164
|
+
no_sync: bool | None = None,
|
|
165
|
+
database: str | None = None,
|
|
164
166
|
) -> None:
|
|
165
167
|
"""Write LineBuilder objects with optional batching and retry.
|
|
166
168
|
|
|
@@ -170,8 +172,17 @@ def write_data(
|
|
|
170
172
|
batch: Combine all lines into one payload via :class:`BatchLines`.
|
|
171
173
|
Set ``False`` to write each line individually.
|
|
172
174
|
retries: Number of extra attempts on failure (``0`` disables retry).
|
|
175
|
+
Effective only when ``no_sync`` is set: the default buffered
|
|
176
|
+
``write`` / ``write_to_db`` never raise at call time.
|
|
173
177
|
base_delay: Base seconds for exponential backoff with jitter.
|
|
174
|
-
no_sync:
|
|
178
|
+
no_sync: When set, writes go through ``write_sync`` /
|
|
179
|
+
``write_sync_to_db`` with this flag (requires InfluxDB 3.8+),
|
|
180
|
+
which write immediately and raise on failure. When ``None``
|
|
181
|
+
(default), the universally available ``write`` / ``write_to_db``
|
|
182
|
+
are used: they only queue lines that are flushed after plugin
|
|
183
|
+
execution completes, so failures are not reported to the caller.
|
|
184
|
+
database: Target database; when ``None``, writes to the trigger's
|
|
185
|
+
database.
|
|
175
186
|
"""
|
|
176
187
|
builders = [builder for builder in line_builders if builder is not None]
|
|
177
188
|
if not builders:
|
|
@@ -180,13 +191,23 @@ def write_data(
|
|
|
180
191
|
payloads = [BatchLines(builders)] if batch else builders
|
|
181
192
|
attempts = max(retries, 0) + 1
|
|
182
193
|
|
|
194
|
+
if no_sync is None:
|
|
195
|
+
if database is not None:
|
|
196
|
+
write_fn = partial(influxdb3_local.write_to_db, database)
|
|
197
|
+
else:
|
|
198
|
+
write_fn = influxdb3_local.write
|
|
199
|
+
elif database is not None:
|
|
200
|
+
write_fn = partial(influxdb3_local.write_sync_to_db, database, no_sync=no_sync)
|
|
201
|
+
else:
|
|
202
|
+
write_fn = partial(influxdb3_local.write_sync, no_sync=no_sync)
|
|
203
|
+
|
|
183
204
|
for payload in payloads:
|
|
184
205
|
for attempt in range(attempts):
|
|
185
206
|
try:
|
|
186
|
-
|
|
207
|
+
write_fn(payload)
|
|
187
208
|
break
|
|
188
209
|
except Exception:
|
|
189
210
|
if attempt == attempts - 1:
|
|
190
211
|
raise
|
|
191
212
|
delay = (2**attempt) * base_delay + random.uniform(0, base_delay)
|
|
192
|
-
time.sleep(delay)
|
|
213
|
+
time.sleep(delay)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/src/influxdata_plugin_utils/cache.py
RENAMED
|
File without changes
|
|
File without changes
|
{influxdata_plugin_utils-0.1.0 → influxdata_plugin_utils-0.2.0}/src/influxdata_plugin_utils/py.typed
RENAMED
|
File without changes
|