meerschaum 2.9.5__py3-none-any.whl → 3.0.0rc2__py3-none-any.whl
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.
- meerschaum/__init__.py +5 -2
- meerschaum/_internal/__init__.py +1 -0
- meerschaum/_internal/arguments/_parse_arguments.py +4 -4
- meerschaum/_internal/arguments/_parser.py +19 -2
- meerschaum/_internal/docs/index.py +49 -2
- meerschaum/_internal/entry.py +6 -6
- meerschaum/_internal/shell/Shell.py +1 -1
- meerschaum/_internal/static.py +356 -0
- meerschaum/actions/api.py +12 -2
- meerschaum/actions/bootstrap.py +7 -7
- meerschaum/actions/edit.py +142 -18
- meerschaum/actions/register.py +137 -6
- meerschaum/actions/show.py +117 -29
- meerschaum/actions/stop.py +4 -1
- meerschaum/actions/sync.py +1 -1
- meerschaum/actions/tag.py +9 -8
- meerschaum/actions/verify.py +5 -8
- meerschaum/api/__init__.py +11 -3
- meerschaum/api/_events.py +39 -2
- meerschaum/api/_oauth2.py +118 -8
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -3
- meerschaum/api/dash/callbacks/custom.py +2 -2
- meerschaum/api/dash/callbacks/dashboard.py +103 -19
- meerschaum/api/dash/callbacks/plugins.py +0 -1
- meerschaum/api/dash/callbacks/register.py +1 -1
- meerschaum/api/dash/callbacks/settings/__init__.py +1 -0
- meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
- meerschaum/api/dash/callbacks/settings/tokens.py +388 -0
- meerschaum/api/dash/components.py +30 -8
- meerschaum/api/dash/keys.py +19 -93
- meerschaum/api/dash/pages/dashboard.py +1 -20
- meerschaum/api/dash/pages/settings/__init__.py +1 -0
- meerschaum/api/dash/pages/settings/password_reset.py +1 -1
- meerschaum/api/dash/pages/settings/tokens.py +55 -0
- meerschaum/api/dash/pipes.py +94 -59
- meerschaum/api/dash/sessions.py +12 -0
- meerschaum/api/dash/tokens.py +606 -0
- meerschaum/api/dash/websockets.py +1 -1
- meerschaum/api/dash/webterm.py +4 -0
- meerschaum/api/models/__init__.py +23 -3
- meerschaum/api/models/_actions.py +22 -0
- meerschaum/api/models/_pipes.py +85 -7
- meerschaum/api/models/_tokens.py +81 -0
- meerschaum/api/resources/templates/termpage.html +12 -0
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +3 -4
- meerschaum/api/routes/_connectors.py +3 -7
- meerschaum/api/routes/_jobs.py +14 -35
- meerschaum/api/routes/_login.py +49 -12
- meerschaum/api/routes/_misc.py +5 -10
- meerschaum/api/routes/_pipes.py +173 -140
- meerschaum/api/routes/_plugins.py +38 -28
- meerschaum/api/routes/_tokens.py +236 -0
- meerschaum/api/routes/_users.py +47 -35
- meerschaum/api/routes/_version.py +3 -3
- meerschaum/config/__init__.py +43 -20
- meerschaum/config/_default.py +43 -6
- meerschaum/config/_edit.py +28 -24
- meerschaum/config/_environment.py +1 -1
- meerschaum/config/_patch.py +6 -6
- meerschaum/config/_paths.py +5 -1
- meerschaum/config/_read_config.py +65 -34
- meerschaum/config/_sync.py +6 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +31 -11
- meerschaum/config/static.py +18 -0
- meerschaum/connectors/_Connector.py +10 -4
- meerschaum/connectors/__init__.py +4 -20
- meerschaum/connectors/api/_APIConnector.py +34 -6
- meerschaum/connectors/api/_actions.py +2 -2
- meerschaum/connectors/api/_jobs.py +1 -1
- meerschaum/connectors/api/_login.py +33 -7
- meerschaum/connectors/api/_misc.py +2 -2
- meerschaum/connectors/api/_pipes.py +16 -31
- meerschaum/connectors/api/_plugins.py +2 -2
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/api/_tokens.py +146 -0
- meerschaum/connectors/api/_users.py +70 -58
- meerschaum/connectors/instance/_InstanceConnector.py +83 -0
- meerschaum/connectors/instance/__init__.py +10 -0
- meerschaum/connectors/instance/_pipes.py +442 -0
- meerschaum/connectors/instance/_plugins.py +151 -0
- meerschaum/connectors/instance/_tokens.py +296 -0
- meerschaum/connectors/instance/_users.py +181 -0
- meerschaum/connectors/parse.py +4 -1
- meerschaum/connectors/sql/_SQLConnector.py +8 -5
- meerschaum/connectors/sql/_cli.py +12 -11
- meerschaum/connectors/sql/_create_engine.py +9 -168
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +156 -190
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +46 -21
- meerschaum/connectors/sql/_users.py +29 -2
- meerschaum/connectors/sql/tables/__init__.py +1 -1
- meerschaum/connectors/valkey/_ValkeyConnector.py +2 -4
- meerschaum/connectors/valkey/_pipes.py +53 -26
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +59 -19
- meerschaum/core/Pipe/_attributes.py +412 -90
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_data.py +96 -18
- meerschaum/core/Pipe/_dtypes.py +48 -18
- meerschaum/core/Pipe/_edit.py +14 -4
- meerschaum/core/Pipe/_fetch.py +1 -1
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +118 -193
- meerschaum/core/Pipe/_verify.py +4 -4
- meerschaum/{plugins → core/Plugin}/_Plugin.py +9 -11
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +220 -0
- meerschaum/core/Token/__init__.py +12 -0
- meerschaum/core/User/_User.py +34 -8
- meerschaum/core/User/__init__.py +9 -1
- meerschaum/core/__init__.py +1 -0
- meerschaum/jobs/_Job.py +3 -2
- meerschaum/jobs/__init__.py +3 -2
- meerschaum/jobs/systemd.py +1 -1
- meerschaum/models/__init__.py +35 -0
- meerschaum/models/pipes.py +247 -0
- meerschaum/models/tokens.py +38 -0
- meerschaum/models/users.py +26 -0
- meerschaum/plugins/__init__.py +22 -7
- meerschaum/plugins/bootstrap.py +2 -1
- meerschaum/utils/_get_pipes.py +68 -27
- meerschaum/utils/daemon/Daemon.py +2 -1
- meerschaum/utils/daemon/__init__.py +30 -2
- meerschaum/utils/dataframe.py +473 -81
- meerschaum/utils/debug.py +15 -15
- meerschaum/utils/dtypes/__init__.py +473 -34
- meerschaum/utils/dtypes/sql.py +368 -28
- meerschaum/utils/formatting/__init__.py +1 -1
- meerschaum/utils/formatting/_pipes.py +5 -4
- meerschaum/utils/formatting/_shell.py +11 -9
- meerschaum/utils/misc.py +246 -148
- meerschaum/utils/packages/__init__.py +10 -27
- meerschaum/utils/packages/_packages.py +41 -34
- meerschaum/utils/pipes.py +181 -0
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +3 -1
- meerschaum/utils/schedule.py +2 -1
- meerschaum/utils/sql.py +121 -44
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +5 -7
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/METADATA +92 -96
- meerschaum-3.0.0rc2.dist-info/RECORD +283 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/WHEEL +1 -1
- meerschaum-3.0.0rc2.dist-info/licenses/NOTICE +2 -0
- meerschaum/api/models/_interfaces.py +0 -15
- meerschaum/api/models/_locations.py +0 -15
- meerschaum/api/models/_metrics.py +0 -15
- meerschaum/config/static/__init__.py +0 -186
- meerschaum-2.9.5.dist-info/RECORD +0 -263
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/zip-safe +0 -0
@@ -17,7 +17,14 @@ from meerschaum.utils.debug import dprint
|
|
17
17
|
from meerschaum.utils.warnings import warn
|
18
18
|
|
19
19
|
### database flavors that can use bulk insert
|
20
|
-
_bulk_flavors = {
|
20
|
+
_bulk_flavors = {
|
21
|
+
'postgresql',
|
22
|
+
'postgis',
|
23
|
+
'timescaledb',
|
24
|
+
'timescaledb-ha',
|
25
|
+
'citus',
|
26
|
+
'mssql',
|
27
|
+
}
|
21
28
|
### flavors that do not support chunks
|
22
29
|
_disallow_chunks_flavors = ['duckdb']
|
23
30
|
_max_chunks_flavors = {'sqlite': 1000}
|
@@ -124,23 +131,28 @@ def read(
|
|
124
131
|
"""
|
125
132
|
if chunks is not None and chunks <= 0:
|
126
133
|
return []
|
134
|
+
|
127
135
|
from meerschaum.utils.sql import sql_item_name, truncate_item_name
|
128
136
|
from meerschaum.utils.dtypes import are_dtypes_equal, coerce_timezone
|
129
137
|
from meerschaum.utils.dtypes.sql import TIMEZONE_NAIVE_FLAVORS
|
130
138
|
from meerschaum.utils.packages import attempt_import, import_pandas
|
131
139
|
from meerschaum.utils.pool import get_pool
|
132
140
|
from meerschaum.utils.dataframe import chunksize_to_npartitions, get_numeric_cols
|
141
|
+
from meerschaum.utils.misc import filter_arguments
|
133
142
|
import warnings
|
134
143
|
import traceback
|
135
144
|
from decimal import Decimal
|
145
|
+
|
136
146
|
pd = import_pandas()
|
137
147
|
dd = None
|
148
|
+
|
138
149
|
is_dask = 'dask' in pd.__name__
|
139
150
|
pandas = attempt_import('pandas')
|
140
151
|
is_dask = dd is not None
|
141
152
|
npartitions = chunksize_to_npartitions(chunksize)
|
142
153
|
if is_dask:
|
143
154
|
chunksize = None
|
155
|
+
|
144
156
|
schema = schema or self.schema
|
145
157
|
utc_dt_cols = [
|
146
158
|
col
|
@@ -151,7 +163,7 @@ def read(
|
|
151
163
|
if dtype and utc_dt_cols and self.flavor in TIMEZONE_NAIVE_FLAVORS:
|
152
164
|
dtype = dtype.copy()
|
153
165
|
for col in utc_dt_cols:
|
154
|
-
dtype[col] = 'datetime64[
|
166
|
+
dtype[col] = 'datetime64[us]'
|
155
167
|
|
156
168
|
pool = get_pool(workers=workers)
|
157
169
|
sqlalchemy = attempt_import("sqlalchemy", lazy=False)
|
@@ -215,26 +227,33 @@ def read(
|
|
215
227
|
else format_sql_query_for_dask(str_query)
|
216
228
|
)
|
217
229
|
|
230
|
+
def _get_chunk_args_kwargs(_chunk):
|
231
|
+
return filter_arguments(
|
232
|
+
chunk_hook,
|
233
|
+
_chunk,
|
234
|
+
workers=workers,
|
235
|
+
chunksize=chunksize,
|
236
|
+
debug=debug,
|
237
|
+
**kw
|
238
|
+
)
|
239
|
+
|
218
240
|
chunk_list = []
|
219
241
|
chunk_hook_results = []
|
220
242
|
def _process_chunk(_chunk, _retry_on_failure: bool = True):
|
221
243
|
if self.flavor in TIMEZONE_NAIVE_FLAVORS:
|
222
244
|
for col in utc_dt_cols:
|
223
|
-
_chunk[col] = coerce_timezone(_chunk[col],
|
245
|
+
_chunk[col] = coerce_timezone(_chunk[col], strip_utc=False)
|
224
246
|
if not as_hook_results:
|
225
247
|
chunk_list.append(_chunk)
|
248
|
+
|
226
249
|
if chunk_hook is None:
|
227
250
|
return None
|
228
251
|
|
252
|
+
chunk_args, chunk_kwargs = _get_chunk_args_kwargs(_chunk)
|
253
|
+
|
229
254
|
result = None
|
230
255
|
try:
|
231
|
-
result = chunk_hook(
|
232
|
-
_chunk,
|
233
|
-
workers=workers,
|
234
|
-
chunksize=chunksize,
|
235
|
-
debug=debug,
|
236
|
-
**kw
|
237
|
-
)
|
256
|
+
result = chunk_hook(*chunk_args, **chunk_kwargs)
|
238
257
|
except Exception:
|
239
258
|
result = False, traceback.format_exc()
|
240
259
|
from meerschaum.utils.formatting import get_console
|
@@ -285,8 +304,16 @@ def read(
|
|
285
304
|
self.engine,
|
286
305
|
**read_sql_query_kwargs
|
287
306
|
)
|
307
|
+
|
288
308
|
to_return = (
|
289
|
-
|
309
|
+
(
|
310
|
+
chunk_generator
|
311
|
+
if not (as_hook_results or chunksize is None)
|
312
|
+
else (
|
313
|
+
_process_chunk(_chunk)
|
314
|
+
for _chunk in chunk_generator
|
315
|
+
)
|
316
|
+
)
|
290
317
|
if as_iterator or chunksize is None
|
291
318
|
else (
|
292
319
|
list(pool.imap(_process_chunk, chunk_generator))
|
@@ -332,9 +359,8 @@ def read(
|
|
332
359
|
try:
|
333
360
|
for chunk in chunk_generator:
|
334
361
|
if chunk_hook is not None:
|
335
|
-
|
336
|
-
|
337
|
-
)
|
362
|
+
chunk_args, chunk_kwargs = _get_chunk_args_kwargs(chunk)
|
363
|
+
chunk_hook_results.append(chunk_hook(*chunk_args, **chunk_kwargs))
|
338
364
|
chunk_list.append(chunk)
|
339
365
|
read_chunks += 1
|
340
366
|
if chunks is not None and read_chunks >= chunks:
|
@@ -349,9 +375,8 @@ def read(
|
|
349
375
|
try:
|
350
376
|
for chunk in chunk_generator:
|
351
377
|
if chunk_hook is not None:
|
352
|
-
|
353
|
-
|
354
|
-
)
|
378
|
+
chunk_args, chunk_kwargs = _get_chunk_args_kwargs(chunk)
|
379
|
+
chunk_hook_results.append(chunk_hook(*chunk_args, **chunk_kwargs))
|
355
380
|
chunk_list.append(chunk)
|
356
381
|
read_chunks += 1
|
357
382
|
if chunks is not None and read_chunks >= chunks:
|
@@ -382,9 +407,8 @@ def read(
|
|
382
407
|
### call the hook on any missed chunks.
|
383
408
|
if chunk_hook is not None and len(chunk_list) > len(chunk_hook_results):
|
384
409
|
for c in chunk_list[len(chunk_hook_results):]:
|
385
|
-
|
386
|
-
|
387
|
-
)
|
410
|
+
chunk_args, chunk_kwargs = _get_chunk_args_kwargs(c)
|
411
|
+
chunk_hook_results.append(chunk_hook(*chunk_args, **chunk_kwargs))
|
388
412
|
|
389
413
|
### chunksize is not None so must iterate
|
390
414
|
if debug:
|
@@ -777,6 +801,7 @@ def to_sql(
|
|
777
801
|
from meerschaum.utils.warnings import error, warn
|
778
802
|
import warnings
|
779
803
|
import functools
|
804
|
+
import traceback
|
780
805
|
|
781
806
|
if name is None:
|
782
807
|
error(f"Name must not be `None` to insert data into {self}.")
|
@@ -1050,7 +1075,7 @@ def to_sql(
|
|
1050
1075
|
except Exception as e:
|
1051
1076
|
if not silent:
|
1052
1077
|
warn(str(e))
|
1053
|
-
success, msg = False,
|
1078
|
+
success, msg = False, traceback.format_exc()
|
1054
1079
|
|
1055
1080
|
end = time.perf_counter()
|
1056
1081
|
if success:
|
@@ -12,6 +12,32 @@ import meerschaum as mrsm
|
|
12
12
|
from meerschaum.utils.typing import SuccessTuple, Optional, Any, Dict, List, Union
|
13
13
|
|
14
14
|
|
15
|
+
def get_users_pipe(self) -> mrsm.Pipe:
|
16
|
+
"""
|
17
|
+
Return the internal metadata pipe for users management.
|
18
|
+
"""
|
19
|
+
return mrsm.Pipe(
|
20
|
+
'mrsm', 'users',
|
21
|
+
temporary=True,
|
22
|
+
static=True,
|
23
|
+
null_indices=False,
|
24
|
+
enforce=False,
|
25
|
+
autoincrement=True,
|
26
|
+
columns={
|
27
|
+
'primary': 'user_id',
|
28
|
+
},
|
29
|
+
dtypes={
|
30
|
+
'user_id': 'int',
|
31
|
+
'username': 'string',
|
32
|
+
'attributes': 'json',
|
33
|
+
'user_type': 'string',
|
34
|
+
},
|
35
|
+
indices={
|
36
|
+
'unique': 'username',
|
37
|
+
},
|
38
|
+
)
|
39
|
+
|
40
|
+
|
15
41
|
def register_user(
|
16
42
|
self,
|
17
43
|
user: mrsm.core.User,
|
@@ -64,7 +90,7 @@ def register_user(
|
|
64
90
|
|
65
91
|
def valid_username(username: str) -> SuccessTuple:
|
66
92
|
"""Verify that a given username is valid."""
|
67
|
-
from meerschaum.
|
93
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
68
94
|
fail_reasons = []
|
69
95
|
|
70
96
|
min_length = STATIC_CONFIG['users']['min_username_length']
|
@@ -104,6 +130,7 @@ def edit_user(
|
|
104
130
|
) -> SuccessTuple:
|
105
131
|
"""Update an existing user's metadata."""
|
106
132
|
from meerschaum.utils.packages import attempt_import
|
133
|
+
from meerschaum.utils.sql import json_flavors
|
107
134
|
sqlalchemy = attempt_import('sqlalchemy', lazy=False)
|
108
135
|
from meerschaum.connectors.sql.tables import get_tables
|
109
136
|
users_tbl = get_tables(mrsm_instance=self, debug=debug)['users']
|
@@ -131,7 +158,7 @@ def edit_user(
|
|
131
158
|
bind_variables['email'] = user.email
|
132
159
|
if user.attributes is not None and user.attributes != {}:
|
133
160
|
bind_variables['attributes'] = (
|
134
|
-
json.dumps(user.attributes) if self.flavor in
|
161
|
+
json.dumps(user.attributes) if self.flavor not in json_flavors
|
135
162
|
else user.attributes
|
136
163
|
)
|
137
164
|
if user.type != '':
|
@@ -231,7 +231,7 @@ def create_schemas(
|
|
231
231
|
"""
|
232
232
|
Create the internal Meerschaum schema on the database.
|
233
233
|
"""
|
234
|
-
from meerschaum.
|
234
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
235
235
|
from meerschaum.utils.packages import attempt_import
|
236
236
|
from meerschaum.utils.sql import sql_item_name, NO_SCHEMA_FLAVORS, SKIP_IF_EXISTS_FLAVORS
|
237
237
|
if conn.flavor in NO_SCHEMA_FLAVORS:
|
@@ -10,19 +10,18 @@ import json
|
|
10
10
|
from datetime import datetime, timezone
|
11
11
|
|
12
12
|
import meerschaum as mrsm
|
13
|
-
from meerschaum.connectors import
|
13
|
+
from meerschaum.connectors import InstanceConnector, make_connector
|
14
14
|
from meerschaum.utils.typing import List, Dict, Any, Optional, Union
|
15
15
|
from meerschaum.utils.warnings import dprint
|
16
16
|
|
17
17
|
|
18
18
|
@make_connector
|
19
|
-
class ValkeyConnector(
|
19
|
+
class ValkeyConnector(InstanceConnector):
|
20
20
|
"""
|
21
21
|
Manage a Valkey instance.
|
22
22
|
|
23
23
|
Build a `ValkeyConnector` from connection attributes or a URI string.
|
24
24
|
"""
|
25
|
-
IS_INSTANCE: bool = True
|
26
25
|
REQUIRED_ATTRIBUTES: List[str] = ['host']
|
27
26
|
OPTIONAL_ATTRIBUTES: List[str] = [
|
28
27
|
'port', 'username', 'password', 'db', 'socket_timeout',
|
@@ -80,7 +79,6 @@ class ValkeyConnector(Connector):
|
|
80
79
|
get_plugin_user_id,
|
81
80
|
get_plugin_username,
|
82
81
|
get_plugin_attributes,
|
83
|
-
get_plugins,
|
84
82
|
delete_plugin,
|
85
83
|
)
|
86
84
|
|
@@ -13,7 +13,7 @@ from meerschaum.utils.typing import SuccessTuple, Any, Union, Optional, Dict, Li
|
|
13
13
|
from meerschaum.utils.misc import string_to_dict
|
14
14
|
from meerschaum.utils.dtypes import json_serialize_value
|
15
15
|
from meerschaum.utils.warnings import warn, dprint
|
16
|
-
from meerschaum.
|
16
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
17
17
|
|
18
18
|
PIPES_TABLE: str = 'mrsm_pipes'
|
19
19
|
PIPES_COUNTER: str = 'mrsm_pipes:counter'
|
@@ -276,7 +276,7 @@ def edit_pipe(
|
|
276
276
|
return False, f"{pipe} is not registered."
|
277
277
|
|
278
278
|
parameters_key = get_pipe_parameters_key(pipe)
|
279
|
-
parameters_str = json.dumps(pipe.
|
279
|
+
parameters_str = json.dumps(pipe.get_parameters(apply_symlinks=False), separators=(',', ':'))
|
280
280
|
self.set(parameters_key, parameters_str)
|
281
281
|
return True, "Success"
|
282
282
|
|
@@ -321,14 +321,40 @@ def drop_pipe(
|
|
321
321
|
-------
|
322
322
|
A `SuccessTuple` indicating success.
|
323
323
|
"""
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
324
|
+
if not pipe.exists(debug=debug):
|
325
|
+
return True, f"{pipe} does not exist, so it was not dropped."
|
326
|
+
|
327
|
+
table_name = self.quote_table(pipe.target)
|
328
|
+
dt_col = pipe.columns.get('datetime', None)
|
329
|
+
|
330
|
+
try:
|
331
|
+
members = (
|
332
|
+
self.client.zrange(table_name, 0, -1)
|
333
|
+
if dt_col
|
334
|
+
else self.client.smembers(table_name)
|
329
335
|
)
|
330
|
-
|
331
|
-
|
336
|
+
|
337
|
+
keys_to_delete = []
|
338
|
+
for member_bytes in members:
|
339
|
+
member_str = member_bytes.decode('utf-8')
|
340
|
+
member_doc = json.loads(member_str)
|
341
|
+
ix_str = member_doc.get('ix')
|
342
|
+
if not ix_str:
|
343
|
+
continue
|
344
|
+
|
345
|
+
ix_doc = string_to_dict(ix_str.replace(COLON, ':'))
|
346
|
+
doc_key = self.get_document_key(ix_doc, list(ix_doc.keys()), table_name)
|
347
|
+
keys_to_delete.append(doc_key)
|
348
|
+
|
349
|
+
if keys_to_delete:
|
350
|
+
batch_size = 1000
|
351
|
+
for i in range(0, len(keys_to_delete), batch_size):
|
352
|
+
batch = keys_to_delete[i:i+batch_size]
|
353
|
+
self.client.delete(*batch)
|
354
|
+
|
355
|
+
except Exception as e:
|
356
|
+
return False, f"Failed to delete documents for {pipe}:\n{e}"
|
357
|
+
|
332
358
|
try:
|
333
359
|
self.drop_table(pipe.target, debug=debug)
|
334
360
|
except Exception as e:
|
@@ -337,7 +363,7 @@ def drop_pipe(
|
|
337
363
|
if 'valkey' not in pipe.parameters:
|
338
364
|
return True, "Success"
|
339
365
|
|
340
|
-
pipe.parameters['valkey']['dtypes'] = {}
|
366
|
+
pipe._attributes['parameters']['valkey']['dtypes'] = {}
|
341
367
|
if not pipe.temporary:
|
342
368
|
edit_success, edit_msg = pipe.edit(debug=debug)
|
343
369
|
if not edit_success:
|
@@ -558,11 +584,7 @@ def sync_pipe(
|
|
558
584
|
|
559
585
|
valkey_dtypes = pipe.parameters.get('valkey', {}).get('dtypes', {})
|
560
586
|
new_dtypes = {
|
561
|
-
str(key): (
|
562
|
-
str(val)
|
563
|
-
if not are_dtypes_equal(str(val), 'datetime')
|
564
|
-
else 'datetime64[ns, UTC]'
|
565
|
-
)
|
587
|
+
str(key): str(val)
|
566
588
|
for key, val in df.dtypes.items()
|
567
589
|
if str(key) not in valkey_dtypes
|
568
590
|
}
|
@@ -571,19 +593,20 @@ def sync_pipe(
|
|
571
593
|
try:
|
572
594
|
df[col] = df[col].astype(typ)
|
573
595
|
except Exception:
|
596
|
+
import traceback
|
597
|
+
traceback.print_exc()
|
574
598
|
valkey_dtypes[col] = 'string'
|
575
599
|
new_dtypes[col] = 'string'
|
576
600
|
df[col] = df[col].astype('string')
|
577
601
|
|
578
602
|
if new_dtypes and (not static or not valkey_dtypes):
|
579
603
|
valkey_dtypes.update(new_dtypes)
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
return edit_success, edit_msg
|
604
|
+
update_success, update_msg = pipe.update_parameters(
|
605
|
+
{'valkey': {'dtypes': valkey_dtypes}},
|
606
|
+
debug=debug,
|
607
|
+
)
|
608
|
+
if not update_success:
|
609
|
+
return False, update_msg
|
587
610
|
|
588
611
|
unseen_df, update_df, delta_df = (
|
589
612
|
pipe.filter_existing(df, include_unchanged_columns=True, debug=debug)
|
@@ -781,7 +804,7 @@ def get_sync_time(
|
|
781
804
|
"""
|
782
805
|
from meerschaum.utils.dtypes import are_dtypes_equal
|
783
806
|
dt_col = pipe.columns.get('datetime', None)
|
784
|
-
dt_typ = pipe.dtypes.get(dt_col, '
|
807
|
+
dt_typ = pipe.dtypes.get(dt_col, 'datetime')
|
785
808
|
if not dt_col:
|
786
809
|
return None
|
787
810
|
|
@@ -789,14 +812,18 @@ def get_sync_time(
|
|
789
812
|
table_name = self.quote_table(pipe.target)
|
790
813
|
try:
|
791
814
|
vals = (
|
792
|
-
self.client.zrevrange(table_name, 0, 0)
|
815
|
+
self.client.zrevrange(table_name, 0, 0, withscores=True)
|
793
816
|
if newest
|
794
|
-
else self.client.zrange(table_name, 0, 0)
|
817
|
+
else self.client.zrange(table_name, 0, 0, withscores=True)
|
795
818
|
)
|
796
819
|
if not vals:
|
797
820
|
return None
|
798
|
-
val = vals[0]
|
821
|
+
val = vals[0][0]
|
822
|
+
if isinstance(val, bytes):
|
823
|
+
val = val.decode('utf-8')
|
799
824
|
except Exception:
|
825
|
+
import traceback
|
826
|
+
traceback.print_exc()
|
800
827
|
return None
|
801
828
|
|
802
829
|
doc = json.loads(val)
|
@@ -127,7 +127,8 @@ def get_plugin_id(
|
|
127
127
|
"""
|
128
128
|
Return a plugin's ID.
|
129
129
|
"""
|
130
|
-
|
130
|
+
user_id = self.get_plugin_user_id(plugin, debug=debug)
|
131
|
+
return plugin.name if user_id is not None else None
|
131
132
|
|
132
133
|
|
133
134
|
def get_plugin_version(
|
@@ -199,31 +200,6 @@ def get_plugin_attributes(
|
|
199
200
|
return {}
|
200
201
|
|
201
202
|
|
202
|
-
def get_plugins(
|
203
|
-
self,
|
204
|
-
user_id: Optional[int] = None,
|
205
|
-
search_term: Optional[str] = None,
|
206
|
-
debug: bool = False,
|
207
|
-
**kw: Any
|
208
|
-
) -> List[str]:
|
209
|
-
"""
|
210
|
-
Return a list of plugin names.
|
211
|
-
"""
|
212
|
-
plugins_pipe = self.get_plugins_pipe()
|
213
|
-
params = {}
|
214
|
-
if user_id:
|
215
|
-
params['user_id'] = user_id
|
216
|
-
|
217
|
-
df = plugins_pipe.get_data(['plugin_name'], params=params, debug=debug)
|
218
|
-
docs = df.to_dict(orient='records')
|
219
|
-
|
220
|
-
return [
|
221
|
-
doc['plugin_name']
|
222
|
-
for doc in docs
|
223
|
-
if (plugin_name := doc['plugin_name']).startswith(search_term or '')
|
224
|
-
]
|
225
|
-
|
226
|
-
|
227
203
|
def delete_plugin(
|
228
204
|
self,
|
229
205
|
plugin: 'mrsm.core.Plugin',
|
meerschaum/core/Pipe/__init__.py
CHANGED
@@ -50,12 +50,16 @@ with correct credentials, as well as a network connection and valid permissions.
|
|
50
50
|
"""
|
51
51
|
|
52
52
|
from __future__ import annotations
|
53
|
+
|
53
54
|
import sys
|
54
55
|
import copy
|
55
|
-
|
56
|
+
|
57
|
+
import meerschaum as mrsm
|
58
|
+
from meerschaum.utils.typing import Optional, Dict, Any, Union, List, InstanceConnector
|
56
59
|
from meerschaum.utils.formatting._pipes import pipe_repr
|
57
60
|
from meerschaum.config import get_config
|
58
61
|
|
62
|
+
|
59
63
|
class Pipe:
|
60
64
|
"""
|
61
65
|
Access Meerschaum pipes via Pipe objects.
|
@@ -89,6 +93,9 @@ class Pipe:
|
|
89
93
|
get_data,
|
90
94
|
get_backtrack_data,
|
91
95
|
get_rowcount,
|
96
|
+
get_data,
|
97
|
+
get_doc,
|
98
|
+
get_value,
|
92
99
|
_get_data_as_iterator,
|
93
100
|
get_chunk_interval,
|
94
101
|
get_chunk_bounds,
|
@@ -104,15 +111,20 @@ class Pipe:
|
|
104
111
|
indexes,
|
105
112
|
dtypes,
|
106
113
|
autoincrement,
|
114
|
+
autotime,
|
107
115
|
upsert,
|
108
116
|
static,
|
109
117
|
tzinfo,
|
110
118
|
enforce,
|
111
119
|
null_indices,
|
120
|
+
mixed_numerics,
|
112
121
|
get_columns,
|
113
122
|
get_columns_types,
|
114
123
|
get_columns_indices,
|
115
124
|
get_indices,
|
125
|
+
get_parameters,
|
126
|
+
get_dtypes,
|
127
|
+
update_parameters,
|
116
128
|
tags,
|
117
129
|
get_id,
|
118
130
|
id,
|
@@ -123,6 +135,9 @@ class Pipe:
|
|
123
135
|
target,
|
124
136
|
_target_legacy,
|
125
137
|
guess_datetime,
|
138
|
+
precision,
|
139
|
+
get_precision,
|
140
|
+
_invalidate_cache,
|
126
141
|
)
|
127
142
|
from ._show import show
|
128
143
|
from ._edit import edit, edit_definition, update
|
@@ -133,11 +148,7 @@ class Pipe:
|
|
133
148
|
filter_existing,
|
134
149
|
_get_chunk_label,
|
135
150
|
get_num_workers,
|
136
|
-
|
137
|
-
_persist_new_numeric_columns,
|
138
|
-
_persist_new_uuid_columns,
|
139
|
-
_persist_new_bytes_columns,
|
140
|
-
_persist_new_geometry_columns,
|
151
|
+
_persist_new_special_columns,
|
141
152
|
)
|
142
153
|
from ._verify import (
|
143
154
|
verify,
|
@@ -168,9 +179,12 @@ class Pipe:
|
|
168
179
|
temporary: bool = False,
|
169
180
|
upsert: Optional[bool] = None,
|
170
181
|
autoincrement: Optional[bool] = None,
|
182
|
+
autotime: Optional[bool] = None,
|
183
|
+
precision: Union[str, Dict[str, Union[str, int]], None] = None,
|
171
184
|
static: Optional[bool] = None,
|
172
185
|
enforce: Optional[bool] = None,
|
173
186
|
null_indices: Optional[bool] = None,
|
187
|
+
mixed_numerics: Optional[bool] = None,
|
174
188
|
mrsm_instance: Optional[Union[str, InstanceConnector]] = None,
|
175
189
|
cache: bool = False,
|
176
190
|
debug: bool = False,
|
@@ -226,6 +240,16 @@ class Pipe:
|
|
226
240
|
autoincrement: Optional[bool], default None
|
227
241
|
If `True`, set `autoincrement` in the parameters.
|
228
242
|
|
243
|
+
autotime: Optional[bool], default None
|
244
|
+
If `True`, set `autotime` in the parameters.
|
245
|
+
|
246
|
+
precision: Union[str, Dict[str, Union[str, int]], None], default None
|
247
|
+
If provided, set `precision` in the parameters.
|
248
|
+
This may be either a string (the precision unit) or a dictionary of in the form
|
249
|
+
`{'unit': <unit>, 'interval': <interval>}`.
|
250
|
+
Default is determined by the `datetime` column dtype
|
251
|
+
(e.g. `datetime64[us]` is `microsecond` precision).
|
252
|
+
|
229
253
|
static: Optional[bool], default None
|
230
254
|
If `True`, set `static` in the parameters.
|
231
255
|
|
@@ -237,6 +261,11 @@ class Pipe:
|
|
237
261
|
Set to `False` if there will be no null values in the index columns.
|
238
262
|
Defaults to `True`.
|
239
263
|
|
264
|
+
mixed_numerics: bool, default None
|
265
|
+
If `True`, integer columns will be converted to `numeric` when floats are synced.
|
266
|
+
Set to `False` to disable this behavior.
|
267
|
+
Defaults to `True`.
|
268
|
+
|
240
269
|
temporary: bool, default False
|
241
270
|
If `True`, prevent instance tables (pipes, users, plugins) from being created.
|
242
271
|
|
@@ -264,7 +293,7 @@ class Pipe:
|
|
264
293
|
if location in ('[None]', 'None'):
|
265
294
|
location = None
|
266
295
|
|
267
|
-
from meerschaum.
|
296
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
268
297
|
negation_prefix = STATIC_CONFIG['system']['fetch_pipes_keys']['negation_prefix']
|
269
298
|
for k in (connector, metric, location, *(tags or [])):
|
270
299
|
if str(k).startswith(negation_prefix):
|
@@ -291,11 +320,13 @@ class Pipe:
|
|
291
320
|
warn(f"The provided parameters are of invalid type '{type(parameters)}'.")
|
292
321
|
self._attributes['parameters'] = {}
|
293
322
|
|
294
|
-
columns = columns or self._attributes.get('parameters', {}).get('columns',
|
295
|
-
if isinstance(columns, list):
|
323
|
+
columns = columns or self._attributes.get('parameters', {}).get('columns', None)
|
324
|
+
if isinstance(columns, (list, tuple)):
|
296
325
|
columns = {str(col): str(col) for col in columns}
|
297
326
|
if isinstance(columns, dict):
|
298
327
|
self._attributes['parameters']['columns'] = columns
|
328
|
+
elif isinstance(columns, str) and 'Pipe(' in columns:
|
329
|
+
pass
|
299
330
|
elif columns is not None:
|
300
331
|
warn(f"The provided columns are of invalid type '{type(columns)}'.")
|
301
332
|
|
@@ -334,6 +365,14 @@ class Pipe:
|
|
334
365
|
if isinstance(autoincrement, bool):
|
335
366
|
self._attributes['parameters']['autoincrement'] = autoincrement
|
336
367
|
|
368
|
+
if isinstance(autotime, bool):
|
369
|
+
self._attributes['parameters']['autotime'] = autotime
|
370
|
+
|
371
|
+
if isinstance(precision, dict):
|
372
|
+
self._attributes['parameters']['precision'] = precision
|
373
|
+
elif isinstance(precision, str):
|
374
|
+
self._attributes['parameters']['precision'] = {'unit': precision}
|
375
|
+
|
337
376
|
if isinstance(static, bool):
|
338
377
|
self._attributes['parameters']['static'] = static
|
339
378
|
|
@@ -343,6 +382,9 @@ class Pipe:
|
|
343
382
|
if isinstance(null_indices, bool):
|
344
383
|
self._attributes['parameters']['null_indices'] = null_indices
|
345
384
|
|
385
|
+
if isinstance(mixed_numerics, bool):
|
386
|
+
self._attributes['parameters']['mixed_numerics'] = mixed_numerics
|
387
|
+
|
346
388
|
### NOTE: The parameters dictionary is {} by default.
|
347
389
|
### A Pipe may be registered without parameters, then edited,
|
348
390
|
### or a Pipe may be registered with parameters set in-memory first.
|
@@ -383,9 +425,7 @@ class Pipe:
|
|
383
425
|
@property
|
384
426
|
def instance_connector(self) -> Union[InstanceConnector, None]:
|
385
427
|
"""
|
386
|
-
The connector
|
387
|
-
May either be of type `meerschaum.connectors.sql.SQLConnector` or
|
388
|
-
`meerschaum.connectors.api.APIConnector`.
|
428
|
+
The instance connector on which this pipe resides.
|
389
429
|
"""
|
390
430
|
if '_instance_connector' not in self.__dict__:
|
391
431
|
from meerschaum.connectors.parse import parse_instance_keys
|
@@ -397,7 +437,7 @@ class Pipe:
|
|
397
437
|
return self._instance_connector
|
398
438
|
|
399
439
|
@property
|
400
|
-
def connector(self) -> Union[
|
440
|
+
def connector(self) -> Union['Connector', None]:
|
401
441
|
"""
|
402
442
|
The connector to the data source.
|
403
443
|
"""
|
@@ -417,7 +457,7 @@ class Pipe:
|
|
417
457
|
return self._connector
|
418
458
|
|
419
459
|
@property
|
420
|
-
def cache_connector(self) -> Union[
|
460
|
+
def cache_connector(self) -> Union['Connector', None]:
|
421
461
|
"""
|
422
462
|
If the pipe was created with `cache=True`, return the connector to the pipe's
|
423
463
|
SQLite database for caching.
|
@@ -438,7 +478,7 @@ class Pipe:
|
|
438
478
|
return self._cache_connector
|
439
479
|
|
440
480
|
@property
|
441
|
-
def cache_pipe(self) -> Union[
|
481
|
+
def cache_pipe(self) -> Union[mrsm.Pipe, None]:
|
442
482
|
"""
|
443
483
|
If the pipe was created with `cache=True`, return another `meerschaum.Pipe` used to
|
444
484
|
manage the local data.
|
@@ -470,10 +510,10 @@ class Pipe:
|
|
470
510
|
self.instance_keys,
|
471
511
|
(self.connector_keys + '_' + self.metric_key + '_cache'),
|
472
512
|
self.location_key,
|
473
|
-
mrsm_instance
|
474
|
-
parameters
|
475
|
-
cache
|
476
|
-
temporary
|
513
|
+
mrsm_instance=self.cache_connector,
|
514
|
+
parameters=_parameters,
|
515
|
+
cache=False,
|
516
|
+
temporary=True,
|
477
517
|
)
|
478
518
|
|
479
519
|
return self._cache_pipe
|