meerschaum 3.0.0rc4__py3-none-any.whl → 3.0.0rc8__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/_internal/arguments/_parser.py +14 -2
- meerschaum/_internal/cli/__init__.py +6 -0
- meerschaum/_internal/cli/daemons.py +103 -0
- meerschaum/_internal/cli/entry.py +220 -0
- meerschaum/_internal/cli/workers.py +435 -0
- meerschaum/_internal/docs/index.py +1 -2
- meerschaum/_internal/entry.py +44 -8
- meerschaum/_internal/shell/Shell.py +115 -24
- meerschaum/_internal/shell/__init__.py +4 -1
- meerschaum/_internal/static.py +4 -1
- meerschaum/_internal/term/TermPageHandler.py +1 -2
- meerschaum/_internal/term/__init__.py +40 -6
- meerschaum/_internal/term/tools.py +33 -8
- meerschaum/actions/__init__.py +6 -4
- meerschaum/actions/api.py +39 -11
- meerschaum/actions/attach.py +1 -0
- meerschaum/actions/delete.py +4 -2
- meerschaum/actions/edit.py +27 -8
- meerschaum/actions/login.py +8 -8
- meerschaum/actions/register.py +13 -7
- meerschaum/actions/reload.py +22 -5
- meerschaum/actions/restart.py +14 -0
- meerschaum/actions/show.py +69 -4
- meerschaum/actions/start.py +135 -14
- meerschaum/actions/stop.py +36 -3
- meerschaum/actions/sync.py +6 -1
- meerschaum/api/__init__.py +35 -13
- meerschaum/api/_events.py +2 -2
- meerschaum/api/_oauth2.py +47 -4
- meerschaum/api/dash/callbacks/dashboard.py +29 -0
- meerschaum/api/dash/callbacks/jobs.py +3 -2
- meerschaum/api/dash/callbacks/login.py +10 -1
- meerschaum/api/dash/callbacks/register.py +9 -2
- meerschaum/api/dash/pages/login.py +2 -2
- meerschaum/api/dash/pipes.py +72 -36
- meerschaum/api/dash/webterm.py +14 -6
- meerschaum/api/models/_pipes.py +7 -1
- meerschaum/api/resources/static/js/terminado.js +3 -0
- meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
- meerschaum/api/resources/templates/termpage.html +1 -0
- meerschaum/api/routes/_jobs.py +23 -11
- meerschaum/api/routes/_login.py +73 -5
- meerschaum/api/routes/_pipes.py +6 -4
- meerschaum/api/routes/_webterm.py +3 -3
- meerschaum/config/__init__.py +60 -13
- meerschaum/config/_default.py +89 -61
- meerschaum/config/_edit.py +10 -8
- meerschaum/config/_formatting.py +2 -0
- meerschaum/config/_patch.py +4 -2
- meerschaum/config/_paths.py +127 -12
- meerschaum/config/_read_config.py +32 -12
- meerschaum/config/_version.py +1 -1
- meerschaum/config/environment.py +262 -0
- meerschaum/config/stack/__init__.py +7 -5
- meerschaum/connectors/_Connector.py +1 -2
- meerschaum/connectors/__init__.py +37 -2
- meerschaum/connectors/api/_APIConnector.py +1 -1
- meerschaum/connectors/api/_jobs.py +11 -0
- meerschaum/connectors/api/_pipes.py +7 -1
- meerschaum/connectors/instance/_plugins.py +9 -1
- meerschaum/connectors/instance/_tokens.py +20 -3
- meerschaum/connectors/instance/_users.py +8 -1
- meerschaum/connectors/parse.py +1 -1
- meerschaum/connectors/sql/_create_engine.py +3 -0
- meerschaum/connectors/sql/_pipes.py +93 -79
- meerschaum/connectors/sql/_users.py +8 -1
- meerschaum/connectors/valkey/_ValkeyConnector.py +3 -3
- meerschaum/connectors/valkey/_pipes.py +7 -5
- meerschaum/core/Pipe/__init__.py +45 -71
- meerschaum/core/Pipe/_attributes.py +66 -90
- meerschaum/core/Pipe/_cache.py +555 -0
- meerschaum/core/Pipe/_clear.py +0 -11
- meerschaum/core/Pipe/_data.py +0 -50
- meerschaum/core/Pipe/_deduplicate.py +0 -13
- meerschaum/core/Pipe/_delete.py +12 -21
- meerschaum/core/Pipe/_drop.py +11 -23
- meerschaum/core/Pipe/_dtypes.py +1 -1
- meerschaum/core/Pipe/_index.py +8 -14
- meerschaum/core/Pipe/_sync.py +12 -18
- meerschaum/core/Plugin/_Plugin.py +7 -1
- meerschaum/core/Token/_Token.py +1 -1
- meerschaum/core/User/_User.py +1 -2
- meerschaum/jobs/_Executor.py +88 -4
- meerschaum/jobs/_Job.py +146 -36
- meerschaum/jobs/systemd.py +7 -2
- meerschaum/plugins/__init__.py +277 -81
- meerschaum/utils/daemon/Daemon.py +197 -42
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
- meerschaum/utils/daemon/RotatingFile.py +63 -36
- meerschaum/utils/daemon/StdinFile.py +53 -13
- meerschaum/utils/daemon/__init__.py +18 -5
- meerschaum/utils/daemon/_names.py +6 -3
- meerschaum/utils/debug.py +34 -4
- meerschaum/utils/dtypes/__init__.py +5 -1
- meerschaum/utils/formatting/__init__.py +4 -1
- meerschaum/utils/formatting/_jobs.py +1 -1
- meerschaum/utils/formatting/_pipes.py +47 -46
- meerschaum/utils/formatting/_shell.py +33 -9
- meerschaum/utils/misc.py +22 -38
- meerschaum/utils/packages/__init__.py +15 -13
- meerschaum/utils/packages/_packages.py +1 -0
- meerschaum/utils/pipes.py +33 -5
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +172 -143
- meerschaum/utils/sql.py +12 -2
- meerschaum/utils/threading.py +42 -0
- meerschaum/utils/venv/__init__.py +2 -0
- meerschaum/utils/warnings.py +19 -13
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/METADATA +3 -1
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/RECORD +116 -110
- meerschaum/config/_environment.py +0 -145
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/WHEEL +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/entry_points.txt +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/licenses/NOTICE +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/top_level.txt +0 -0
- {meerschaum-3.0.0rc4.dist-info → meerschaum-3.0.0rc8.dist-info}/zip-safe +0 -0
meerschaum/config/_paths.py
CHANGED
@@ -9,10 +9,17 @@ Define file paths
|
|
9
9
|
from __future__ import annotations
|
10
10
|
|
11
11
|
from pathlib import Path
|
12
|
-
import
|
13
|
-
|
12
|
+
import contextlib
|
13
|
+
import os
|
14
|
+
import platform
|
15
|
+
import sys
|
16
|
+
import json
|
17
|
+
|
18
|
+
from typing import Union, List
|
19
|
+
|
14
20
|
from meerschaum._internal.static import STATIC_CONFIG
|
15
21
|
|
22
|
+
|
16
23
|
DOT_CONFIG_DIR_PATH = Path(
|
17
24
|
os.environ.get('XDG_CONFIG_HOME', Path.home() / '.config')
|
18
25
|
if platform.system() != 'Windows'
|
@@ -71,7 +78,7 @@ if ENVIRONMENT_PLUGINS_DIR in os.environ:
|
|
71
78
|
if path_str
|
72
79
|
]
|
73
80
|
)
|
74
|
-
except Exception
|
81
|
+
except Exception:
|
75
82
|
PLUGINS_DIR_PATHS = []
|
76
83
|
|
77
84
|
if not PLUGINS_DIR_PATHS:
|
@@ -82,20 +89,23 @@ if ENVIRONMENT_PLUGINS_DIR in os.environ:
|
|
82
89
|
f"`export {ENVIRONMENT_PLUGINS_DIR}=./plugins:/another/path/to/plugins`\n\n"
|
83
90
|
"or a JSON-encoded path list:\n\n"
|
84
91
|
f"`export {ENVIRONMENT_PLUGINS_DIR}=" + "'[\"./plugins\", \"/another/path/to/plugins\"]'`"
|
85
|
-
|
92
|
+
"",
|
86
93
|
)
|
87
94
|
sys.exit(1)
|
88
95
|
else:
|
89
96
|
PLUGINS_DIR_PATHS = [_ROOT_DIR_PATH / 'plugins']
|
90
97
|
|
91
98
|
### Remove duplicate plugins paths.
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
+
def _process_plugins_dir_paths(plugins_dir_paths: List[Path]):
|
100
|
+
_seen_plugins_paths, _plugins_paths_to_remove = set(), set()
|
101
|
+
for _plugin_path in plugins_dir_paths:
|
102
|
+
if _plugin_path in _seen_plugins_paths:
|
103
|
+
_plugins_paths_to_remove.add(_plugin_path)
|
104
|
+
_seen_plugins_paths.add(_plugin_path)
|
105
|
+
for _plugin_path in _plugins_paths_to_remove:
|
106
|
+
plugins_dir_paths.remove(_plugin_path)
|
107
|
+
_process_plugins_dir_paths(PLUGINS_DIR_PATHS)
|
108
|
+
|
99
109
|
|
100
110
|
ENVIRONMENT_VENVS_DIR = STATIC_CONFIG['environment']['venvs']
|
101
111
|
if ENVIRONMENT_VENVS_DIR in os.environ:
|
@@ -144,6 +154,8 @@ paths = {
|
|
144
154
|
'API_SECRET_KEY_PATH' : ('{API_CONFIG_RESOURCES_PATH}', '.api_secret_key'),
|
145
155
|
'API_UVICORN_RESOURCES_PATH' : ('{API_CONFIG_RESOURCES_PATH}', 'uvicorn'),
|
146
156
|
'API_UVICORN_CONFIG_PATH' : ('{API_UVICORN_RESOURCES_PATH}', '.thread_config.json'),
|
157
|
+
|
158
|
+
'WEBTERM_INTERNAL_RESOURCES_PATH': ('{INTERNAL_RESOURCES_PATH}', 'webterm'),
|
147
159
|
|
148
160
|
'CACHE_RESOURCES_PATH' : ('{ROOT_DIR_PATH}', '.cache'),
|
149
161
|
'PIPES_CACHE_RESOURCES_PATH' : ('{CACHE_RESOURCES_PATH}', 'pipes'),
|
@@ -151,12 +163,16 @@ paths = {
|
|
151
163
|
'VENVS_CACHE_RESOURCES_PATH' : ('{CACHE_RESOURCES_PATH}', 'venvs'),
|
152
164
|
'SQL_CONN_CACHE_RESOURCES_PATH' : ('{CACHE_RESOURCES_PATH}', 'sql'),
|
153
165
|
|
166
|
+
'CLI_RESOURCES_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'cli'),
|
167
|
+
'CLI_LOGS_RESOURCES_PATH' : ('{CLI_RESOURCES_PATH}', 'logs'),
|
168
|
+
|
154
169
|
'PLUGINS_RESOURCES_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'plugins'),
|
155
170
|
'PLUGINS_INTERNAL_LOCK_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'plugins.lock'),
|
156
171
|
'PLUGINS_PACKAGES_INTERNAL_PATH' : ('{INTERNAL_RESOURCES_PATH}', 'packaged_plugins'),
|
157
172
|
'PLUGINS_ARCHIVES_RESOURCES_PATH': ('{PLUGINS_RESOURCES_PATH}', '.archives'),
|
158
173
|
'PLUGINS_TEMP_RESOURCES_PATH' : ('{PLUGINS_RESOURCES_PATH}', '.tmp'),
|
159
174
|
'PLUGINS_INIT_PATH' : ('{PLUGINS_RESOURCES_PATH}', '__init__.py'),
|
175
|
+
'PLUGINS_INJECTED_RESOURCES_PATH': ('{PLUGINS_RESOURCES_PATH}', '.injected'),
|
160
176
|
|
161
177
|
'DB_RESOURCES_PATH' : ('{ROOT_DIR_PATH}', 'db'),
|
162
178
|
'DB_INIT_RESOURCES_PATH' : ('{DB_RESOURCES_PATH}', 'initdb'),
|
@@ -197,11 +213,110 @@ paths = {
|
|
197
213
|
|
198
214
|
def set_root(root: Union[Path, str]):
|
199
215
|
"""Modify the value of `ROOT_DIR_PATH`."""
|
200
|
-
paths['ROOT_DIR_PATH'] = Path(root).resolve()
|
216
|
+
paths['ROOT_DIR_PATH'] = Path(root).resolve().as_posix()
|
201
217
|
for path_name, path_parts in paths.items():
|
202
218
|
if isinstance(path_parts, tuple) and path_parts[0] == '{ROOT_DIR_PATH}':
|
203
219
|
globals()[path_name] = __getattr__(path_name)
|
204
220
|
|
221
|
+
def set_plugins_dir_paths(plugins_dir_paths: Union[List[Path], Path, str]):
|
222
|
+
"""Modify the value of `PLUGINS_DIR_PATHS`."""
|
223
|
+
global PLUGINS_DIR_PATHS
|
224
|
+
if isinstance(plugins_dir_paths, str):
|
225
|
+
if plugins_dir_paths.strip() and plugins_dir_paths.lstrip()[0] == '[':
|
226
|
+
plugins_dir_paths = json.loads(plugins_dir_paths)
|
227
|
+
else:
|
228
|
+
plugins_dir_paths = plugins_dir_paths.split(':')
|
229
|
+
plugins_dir_paths = [Path(_path).resolve() for _path in plugins_dir_paths]
|
230
|
+
if isinstance(plugins_dir_paths, Path):
|
231
|
+
plugins_dir_paths = [plugins_dir_paths.resolve()]
|
232
|
+
|
233
|
+
PLUGINS_DIR_PATHS = plugins_dir_paths
|
234
|
+
_process_plugins_dir_paths(plugins_dir_paths)
|
235
|
+
|
236
|
+
def set_venvs_dir_path(venvs_dir_path: Union[str, Path]):
|
237
|
+
"""Modify the value of `VIRTENV_RESOURCES_PATH`."""
|
238
|
+
paths['VIRTENV_RESOURCES_PATH'] = Path(venvs_dir_path).resolve().as_posix()
|
239
|
+
|
240
|
+
def set_config_dir_path(config_dir_path: Union[str, Path]):
|
241
|
+
paths['CONFIG_DIR_PATH'] = Path(config_dir_path).resolve().as_posix()
|
242
|
+
|
243
|
+
|
244
|
+
@contextlib.contextmanager
|
245
|
+
def replace_root_dir(root_dir_path: Union[str, Path, None]):
|
246
|
+
"""
|
247
|
+
Temporarily replace the root directory path.
|
248
|
+
"""
|
249
|
+
if root_dir_path is None:
|
250
|
+
try:
|
251
|
+
yield
|
252
|
+
finally:
|
253
|
+
return
|
254
|
+
|
255
|
+
old_root = paths.get('ROOT_DIR_PATH', _ROOT_DIR_PATH)
|
256
|
+
set_root(root_dir_path)
|
257
|
+
|
258
|
+
try:
|
259
|
+
yield
|
260
|
+
finally:
|
261
|
+
set_root(old_root)
|
262
|
+
|
263
|
+
@contextlib.contextmanager
|
264
|
+
def replace_plugins_dir_paths(plugins_dir_paths: Union[List[Path], None]):
|
265
|
+
"""
|
266
|
+
Temporarily replace the plugins directory paths.
|
267
|
+
"""
|
268
|
+
if plugins_dir_paths is None:
|
269
|
+
try:
|
270
|
+
yield
|
271
|
+
finally:
|
272
|
+
return
|
273
|
+
|
274
|
+
old_plugins_dir_paths = PLUGINS_DIR_PATHS
|
275
|
+
set_plugins_dir_paths(plugins_dir_paths)
|
276
|
+
|
277
|
+
try:
|
278
|
+
yield
|
279
|
+
finally:
|
280
|
+
set_plugins_dir_paths(old_plugins_dir_paths)
|
281
|
+
|
282
|
+
@contextlib.contextmanager
|
283
|
+
def replace_venvs_dir_path(venvs_dir_path: Union[Path, None]):
|
284
|
+
"""
|
285
|
+
Temporarily replace the virtual environments directory path.
|
286
|
+
"""
|
287
|
+
if venvs_dir_path is None:
|
288
|
+
try:
|
289
|
+
yield
|
290
|
+
finally:
|
291
|
+
return
|
292
|
+
|
293
|
+
old_venvs_dir_path = paths.get('VIRTENV_RESOURCES_PATH', _VENVS_DIR_PATH)
|
294
|
+
set_venvs_dir_path(venvs_dir_path)
|
295
|
+
|
296
|
+
try:
|
297
|
+
yield
|
298
|
+
finally:
|
299
|
+
set_venvs_dir_path(old_venvs_dir_path)
|
300
|
+
|
301
|
+
@contextlib.contextmanager
|
302
|
+
def replace_config_dir_path(config_dir_path: Union[Path, None]):
|
303
|
+
"""
|
304
|
+
Temporarily replace the config directory path.
|
305
|
+
"""
|
306
|
+
if config_dir_path is None:
|
307
|
+
try:
|
308
|
+
yield
|
309
|
+
finally:
|
310
|
+
return
|
311
|
+
|
312
|
+
old_config_dir_path = paths.get('CONFIG_DIR_PATH', _CONFIG_DIR_PATH)
|
313
|
+
set_config_dir_path(config_dir_path)
|
314
|
+
|
315
|
+
try:
|
316
|
+
yield
|
317
|
+
finally:
|
318
|
+
set_config_dir_path(old_config_dir_path)
|
319
|
+
|
205
320
|
|
206
321
|
def __getattr__(name: str) -> Path:
|
207
322
|
if name not in paths:
|
@@ -8,7 +8,9 @@ Import the config yaml file
|
|
8
8
|
from __future__ import annotations
|
9
9
|
import pathlib
|
10
10
|
|
11
|
+
import meerschaum as mrsm
|
11
12
|
from meerschaum.utils.typing import Optional, Dict, Any, List, Tuple, Union
|
13
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
12
14
|
|
13
15
|
|
14
16
|
def read_config(
|
@@ -53,7 +55,6 @@ def read_config(
|
|
53
55
|
import itertools
|
54
56
|
from meerschaum.utils.yaml import yaml, _yaml
|
55
57
|
from meerschaum.config._paths import CONFIG_DIR_PATH
|
56
|
-
from meerschaum._internal.static import STATIC_CONFIG
|
57
58
|
from meerschaum.config._patch import apply_patch_to_config
|
58
59
|
if directory is None:
|
59
60
|
directory = CONFIG_DIR_PATH
|
@@ -106,20 +107,25 @@ def read_config(
|
|
106
107
|
### If default config contains symlinks, add them to the config to write.
|
107
108
|
try:
|
108
109
|
_default_symlinks = _default_dict[symlinks_key][mk]
|
109
|
-
except
|
110
|
+
except KeyError:
|
110
111
|
_default_symlinks = {}
|
112
|
+
|
111
113
|
config[mk] = _default_dict[mk]
|
112
114
|
if _default_symlinks:
|
113
115
|
if symlinks_key not in config:
|
114
116
|
config[symlinks_key] = {}
|
117
|
+
if symlinks_key not in config_to_write:
|
118
|
+
config_to_write[symlinks_key] = {}
|
119
|
+
|
115
120
|
if mk not in config[symlinks_key]:
|
116
121
|
config[symlinks_key][mk] = {}
|
122
|
+
if mk not in config_to_write[symlinks_key]:
|
123
|
+
config_to_write[symlinks_key][mk] = {}
|
124
|
+
|
117
125
|
config[symlinks_key][mk] = apply_patch_to_config(
|
118
126
|
config[symlinks_key][mk],
|
119
127
|
_default_symlinks
|
120
128
|
)
|
121
|
-
if symlinks_key not in config_to_write:
|
122
|
-
config_to_write[symlinks_key] = {}
|
123
129
|
config_to_write[symlinks_key][mk] = config[symlinks_key][mk]
|
124
130
|
|
125
131
|
### Write the default key.
|
@@ -387,10 +393,16 @@ def search_and_substitute_config(
|
|
387
393
|
s[_keys[-1]] = _pattern
|
388
394
|
|
389
395
|
from meerschaum.config._patch import apply_patch_to_config
|
390
|
-
from meerschaum._internal.static import STATIC_CONFIG
|
391
396
|
symlinks_key = STATIC_CONFIG['config']['symlinks_key']
|
392
|
-
if
|
393
|
-
|
397
|
+
if symlinks:
|
398
|
+
if symlinks_key not in parsed_config:
|
399
|
+
parsed_config[symlinks_key] = symlinks
|
400
|
+
else:
|
401
|
+
parsed_config[symlinks_key] = apply_patch_to_config(
|
402
|
+
parsed_config[symlinks_key],
|
403
|
+
symlinks,
|
404
|
+
warn=False,
|
405
|
+
)
|
394
406
|
|
395
407
|
return parsed_config
|
396
408
|
|
@@ -409,15 +421,24 @@ def revert_symlinks_config(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
409
421
|
-------
|
410
422
|
A configuration dictionary with `_symlinks` re-applied.
|
411
423
|
"""
|
412
|
-
|
424
|
+
import copy
|
413
425
|
from meerschaum._internal.static import STATIC_CONFIG
|
426
|
+
|
414
427
|
symlinks_key = STATIC_CONFIG['config']['symlinks_key']
|
415
428
|
if symlinks_key not in config:
|
416
429
|
return config
|
417
430
|
|
418
|
-
|
419
|
-
|
420
|
-
|
431
|
+
reverted_config = copy.deepcopy(config)
|
432
|
+
symlinks_config = reverted_config.pop(symlinks_key)
|
433
|
+
|
434
|
+
def deep_patch(target_dict, patch_dict):
|
435
|
+
for key, value in patch_dict.items():
|
436
|
+
if isinstance(value, dict) and key in target_dict and isinstance(target_dict[key], dict):
|
437
|
+
deep_patch(target_dict[key], value)
|
438
|
+
else:
|
439
|
+
target_dict[key] = value
|
440
|
+
|
441
|
+
deep_patch(reverted_config, symlinks_config)
|
421
442
|
return reverted_config
|
422
443
|
|
423
444
|
|
@@ -462,7 +483,6 @@ def get_keyfile_path(
|
|
462
483
|
)
|
463
484
|
except IndexError:
|
464
485
|
if create_new:
|
465
|
-
from meerschaum._internal.static import STATIC_CONFIG
|
466
486
|
default_filetype = STATIC_CONFIG['config']['default_filetype']
|
467
487
|
return pathlib.Path(os.path.join(directory, key + '.' + default_filetype))
|
468
488
|
return None
|
meerschaum/config/_version.py
CHANGED
@@ -0,0 +1,262 @@
|
|
1
|
+
#! /usr/bin/env python3
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# vim:fenc=utf-8
|
4
|
+
|
5
|
+
"""
|
6
|
+
Patch the runtime configuration from environment variables.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import os
|
10
|
+
import re
|
11
|
+
import json
|
12
|
+
import contextlib
|
13
|
+
import copy
|
14
|
+
import pathlib
|
15
|
+
|
16
|
+
from meerschaum.utils.typing import List, Union, Dict, Any, Optional
|
17
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
18
|
+
|
19
|
+
|
20
|
+
def apply_environment_patches(env: Optional[Dict[str, Any]] = None) -> None:
|
21
|
+
"""
|
22
|
+
Apply patches defined in `MRSM_CONFIG` and `MRSM_PATCH`.
|
23
|
+
"""
|
24
|
+
config_var = STATIC_CONFIG['environment']['config']
|
25
|
+
patch_var = STATIC_CONFIG['environment']['patch']
|
26
|
+
apply_environment_config(config_var, env=env)
|
27
|
+
apply_environment_config(patch_var, env=env)
|
28
|
+
|
29
|
+
|
30
|
+
def apply_environment_config(env_var: str, env: Optional[Dict[str, Any]] = None) -> None:
|
31
|
+
"""
|
32
|
+
Parse a dictionary (simple or JSON) from an environment variable
|
33
|
+
and apply it to the current configuration.
|
34
|
+
"""
|
35
|
+
from meerschaum.config import get_config, set_config, _config
|
36
|
+
from meerschaum.config._patch import apply_patch_to_config
|
37
|
+
|
38
|
+
env = env if env is not None else os.environ
|
39
|
+
|
40
|
+
if env_var not in env:
|
41
|
+
return
|
42
|
+
|
43
|
+
from meerschaum.utils.misc import string_to_dict
|
44
|
+
try:
|
45
|
+
_patch = string_to_dict(str(os.environ[env_var]).lstrip())
|
46
|
+
except Exception:
|
47
|
+
_patch = None
|
48
|
+
|
49
|
+
error_msg = (
|
50
|
+
f"Environment variable {env_var} is set but cannot be parsed.\n"
|
51
|
+
f"Unset {env_var} or change to JSON or simplified dictionary format "
|
52
|
+
"(see --help, under params for formatting)\n"
|
53
|
+
f"{env_var} is set to:\n{os.environ[env_var]}\n"
|
54
|
+
f"Skipping patching os environment into config..."
|
55
|
+
)
|
56
|
+
|
57
|
+
if not isinstance(_patch, dict):
|
58
|
+
print(error_msg)
|
59
|
+
return
|
60
|
+
|
61
|
+
valids = []
|
62
|
+
|
63
|
+
def load_key(key: str) -> Union[Dict[str, Any], None]:
|
64
|
+
try:
|
65
|
+
c = get_config(key, warn=False)
|
66
|
+
except Exception:
|
67
|
+
c = None
|
68
|
+
return c
|
69
|
+
|
70
|
+
### This was multi-threaded, but I ran into all sorts of locking issues.
|
71
|
+
keys = list(_patch.keys())
|
72
|
+
for key in keys:
|
73
|
+
_ = load_key(key)
|
74
|
+
|
75
|
+
### Load and patch config files.
|
76
|
+
set_config(
|
77
|
+
apply_patch_to_config(
|
78
|
+
_config(),
|
79
|
+
_patch,
|
80
|
+
)
|
81
|
+
)
|
82
|
+
|
83
|
+
|
84
|
+
def apply_environment_uris(env: Optional[Dict[str, Any]] = None) -> None:
|
85
|
+
"""
|
86
|
+
Patch temporary connectors defined in environment variables which start with
|
87
|
+
`MRSM_SQL_` or `MRSM_API_`.
|
88
|
+
"""
|
89
|
+
for env_var in get_connector_env_vars(env=env):
|
90
|
+
apply_connector_uri(env_var, env=env)
|
91
|
+
|
92
|
+
|
93
|
+
def get_connector_env_regex() -> str:
|
94
|
+
"""
|
95
|
+
Return the regex pattern for valid environment variable names for instance connectors.
|
96
|
+
"""
|
97
|
+
return STATIC_CONFIG['environment']['uri_regex']
|
98
|
+
|
99
|
+
|
100
|
+
def get_connector_env_vars(env: Optional[Dict[str, Any]] = None) -> List[str]:
|
101
|
+
"""
|
102
|
+
Get the names of the environment variables which match the Meerschaum connector regex.
|
103
|
+
|
104
|
+
Examples
|
105
|
+
--------
|
106
|
+
>>> get_connector_environment_vars()
|
107
|
+
['MRSM_SQL_FOO']
|
108
|
+
"""
|
109
|
+
uri_regex = get_connector_env_regex()
|
110
|
+
env_vars = []
|
111
|
+
|
112
|
+
env = env if env is not None else os.environ
|
113
|
+
|
114
|
+
for env_var in env:
|
115
|
+
matched = re.match(uri_regex, env_var)
|
116
|
+
if matched is None:
|
117
|
+
continue
|
118
|
+
if env_var in STATIC_CONFIG['environment'].values():
|
119
|
+
continue
|
120
|
+
env_vars.append(env_var)
|
121
|
+
|
122
|
+
return env_vars
|
123
|
+
|
124
|
+
|
125
|
+
def apply_connector_uri(env_var: str, env: Optional[Dict[str, Any]] = None) -> None:
|
126
|
+
"""
|
127
|
+
Parse and validate a URI obtained from an environment variable.
|
128
|
+
"""
|
129
|
+
from meerschaum.config import get_config, set_config, _config
|
130
|
+
from meerschaum.config._patch import apply_patch_to_config
|
131
|
+
from meerschaum.config._read_config import search_and_substitute_config
|
132
|
+
from meerschaum.utils.warnings import warn
|
133
|
+
|
134
|
+
env = env if env is not None else os.environ
|
135
|
+
|
136
|
+
if env_var not in env:
|
137
|
+
return
|
138
|
+
|
139
|
+
uri_regex = get_connector_env_regex()
|
140
|
+
matched = re.match(uri_regex, env_var)
|
141
|
+
groups = matched.groups()
|
142
|
+
typ, label = groups[0].lower(), groups[1].lower()
|
143
|
+
if not typ or not label:
|
144
|
+
return
|
145
|
+
|
146
|
+
uri = env[env_var]
|
147
|
+
|
148
|
+
if uri.lstrip().startswith('{') and uri.rstrip().endswith('}'):
|
149
|
+
try:
|
150
|
+
conn_attrs = json.loads(uri)
|
151
|
+
except Exception:
|
152
|
+
warn(f"Unable to parse JSON for environment connector '{typ}:{label}'.")
|
153
|
+
conn_attrs = {'uri': uri}
|
154
|
+
else:
|
155
|
+
conn_attrs = {'uri': uri}
|
156
|
+
|
157
|
+
set_config(
|
158
|
+
apply_patch_to_config(
|
159
|
+
{'meerschaum': get_config('meerschaum')},
|
160
|
+
{'meerschaum': {'connectors': {typ: {label: conn_attrs}}}},
|
161
|
+
)
|
162
|
+
)
|
163
|
+
|
164
|
+
|
165
|
+
def get_env_vars(env: Optional[Dict[str, Any]] = None) -> List[str]:
|
166
|
+
"""
|
167
|
+
Return all environment variables which begin with `'MRSM_'`.
|
168
|
+
"""
|
169
|
+
prefix = STATIC_CONFIG['environment']['prefix']
|
170
|
+
env = env if env is not None else os.environ
|
171
|
+
return sorted([env_var for env_var in env if env_var.startswith(prefix)])
|
172
|
+
|
173
|
+
|
174
|
+
@contextlib.contextmanager
|
175
|
+
def replace_env(env: Union[Dict[str, Any], None]):
|
176
|
+
"""
|
177
|
+
Temporarily replace environment variables and current configuration.
|
178
|
+
|
179
|
+
Parameters
|
180
|
+
----------
|
181
|
+
env: Dict[str, Any]
|
182
|
+
The new environment dictionary to be patched on `os.environ`.
|
183
|
+
"""
|
184
|
+
if env is None:
|
185
|
+
try:
|
186
|
+
yield
|
187
|
+
finally:
|
188
|
+
return
|
189
|
+
|
190
|
+
from meerschaum.config import _config, set_config
|
191
|
+
from meerschaum.config.paths import (
|
192
|
+
set_root,
|
193
|
+
set_plugins_dir_paths,
|
194
|
+
set_venvs_dir_path,
|
195
|
+
set_config_dir_path,
|
196
|
+
ROOT_DIR_PATH,
|
197
|
+
PLUGINS_DIR_PATHS,
|
198
|
+
VIRTENV_RESOURCES_PATH,
|
199
|
+
CONFIG_DIR_PATH,
|
200
|
+
)
|
201
|
+
|
202
|
+
old_environ = dict(os.environ)
|
203
|
+
old_config = copy.deepcopy(_config())
|
204
|
+
old_root_dir_path = ROOT_DIR_PATH
|
205
|
+
old_plugins_dir_paths = PLUGINS_DIR_PATHS
|
206
|
+
old_venvs_dir_path = VIRTENV_RESOURCES_PATH
|
207
|
+
old_config_dir_path = CONFIG_DIR_PATH
|
208
|
+
|
209
|
+
os.environ.update(env)
|
210
|
+
|
211
|
+
root_dir_env_var = STATIC_CONFIG['environment']['root']
|
212
|
+
plugins_dir_env_var = STATIC_CONFIG['environment']['plugins']
|
213
|
+
config_dir_env_var = STATIC_CONFIG['environment']['config_dir']
|
214
|
+
venvs_dir_env_var = STATIC_CONFIG['environment']['venvs']
|
215
|
+
|
216
|
+
replaced_root = False
|
217
|
+
if root_dir_env_var in env:
|
218
|
+
root_dir_path = pathlib.Path(env[root_dir_env_var])
|
219
|
+
set_root(root_dir_path)
|
220
|
+
replaced_root = True
|
221
|
+
|
222
|
+
replaced_plugins = False
|
223
|
+
if plugins_dir_env_var in env:
|
224
|
+
plugins_dir_paths = env[plugins_dir_env_var]
|
225
|
+
set_plugins_dir_paths(plugins_dir_paths)
|
226
|
+
replaced_plugins = True
|
227
|
+
|
228
|
+
replaced_venvs = False
|
229
|
+
if venvs_dir_env_var in env:
|
230
|
+
venv_dir_path = pathlib.Path(env[venvs_dir_env_var])
|
231
|
+
set_venvs_dir_path(venv_dir_path)
|
232
|
+
replaced_venvs = True
|
233
|
+
|
234
|
+
replaced_config_dir = False
|
235
|
+
if config_dir_env_var in env:
|
236
|
+
config_dir_path = pathlib.Path(env[config_dir_env_var])
|
237
|
+
set_config_dir_path(config_dir_path)
|
238
|
+
replaced_config_dir = True
|
239
|
+
|
240
|
+
apply_environment_patches(env)
|
241
|
+
apply_environment_uris(env)
|
242
|
+
|
243
|
+
try:
|
244
|
+
yield
|
245
|
+
finally:
|
246
|
+
os.environ.clear()
|
247
|
+
os.environ.update(old_environ)
|
248
|
+
|
249
|
+
if replaced_root:
|
250
|
+
set_root(old_root_dir_path)
|
251
|
+
|
252
|
+
if replaced_plugins:
|
253
|
+
set_plugins_dir_paths(old_plugins_dir_paths)
|
254
|
+
|
255
|
+
if replaced_venvs:
|
256
|
+
set_venvs_dir_path(old_venvs_dir_path)
|
257
|
+
|
258
|
+
if replaced_config_dir:
|
259
|
+
set_config_dir_path(old_config_dir_path)
|
260
|
+
|
261
|
+
_config().clear()
|
262
|
+
set_config(old_config)
|
@@ -56,19 +56,22 @@ env_dict['MEERSCHAUM_API_CONFIG'] = json.dumps(
|
|
56
56
|
{
|
57
57
|
'meerschaum': 'MRSM{!meerschaum}',
|
58
58
|
'system': 'MRSM{!system}',
|
59
|
+
'api': 'MRSM{!api}',
|
59
60
|
},
|
60
61
|
indent=4,
|
61
62
|
).replace(
|
62
63
|
'"MRSM{!system}"', 'MRSM{!system}'
|
63
64
|
).replace(
|
64
|
-
'"MRSM{!meerschaum}"', 'MRSM{!meerschaum}'
|
65
|
+
'"MRSM{!meerschaum}"', 'MRSM{!meerschaum}'
|
66
|
+
).replace(
|
67
|
+
'"MRSM{!api}"', 'MRSM{!api}'
|
65
68
|
)
|
66
69
|
|
67
70
|
volumes = {
|
68
71
|
'api_root': '/meerschaum',
|
69
|
-
'meerschaum_db_data': '/
|
72
|
+
'meerschaum_db_data': '/home/postgres/pgdata',
|
70
73
|
'grafana_storage': '/var/lib/grafana',
|
71
|
-
'valkey_data': '/
|
74
|
+
'valkey_data': '/valkey/data',
|
72
75
|
}
|
73
76
|
networks = {
|
74
77
|
'frontend': None,
|
@@ -122,7 +125,6 @@ default_docker_compose_config = {
|
|
122
125
|
'POSTGRES_DB': '<DOLLAR>POSTGRES_DB',
|
123
126
|
'POSTGRES_PASSWORD': '<DOLLAR>POSTGRES_PASSWORD',
|
124
127
|
'ALLOW_IP_RANGE': env_dict['ALLOW_IP_RANGE'],
|
125
|
-
# 'POSTGRES_INITDB_ARGS': '-c max_connections=1000 -c shared_buffers=1024MB -c max_prepared_transactions=100'
|
126
128
|
},
|
127
129
|
'command': 'postgres -c max_connections=1000 -c shared_buffers=1024MB -c max_prepared_transactions=100',
|
128
130
|
'healthcheck': {
|
@@ -184,7 +186,7 @@ default_docker_compose_config = {
|
|
184
186
|
],
|
185
187
|
},
|
186
188
|
'valkey': {
|
187
|
-
'image': '
|
189
|
+
'image': 'valkey/valkey:latest',
|
188
190
|
'restart': 'always',
|
189
191
|
'environment': {
|
190
192
|
'VALKEY_PASSWORD': '<DOLLAR>VALKEY_PASSWORD',
|
@@ -152,8 +152,7 @@ class Connector(metaclass=abc.ABCMeta):
|
|
152
152
|
------
|
153
153
|
An error if any of the required attributes are missing.
|
154
154
|
"""
|
155
|
-
from meerschaum.utils.warnings import error
|
156
|
-
from meerschaum.utils.debug import dprint
|
155
|
+
from meerschaum.utils.warnings import error
|
157
156
|
from meerschaum.utils.misc import items_str
|
158
157
|
if required_attributes is None:
|
159
158
|
required_attributes = ['type', 'label']
|