meerschaum 3.0.0rc3__py3-none-any.whl → 3.0.0rc7__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 +434 -0
- meerschaum/_internal/docs/index.py +1 -2
- meerschaum/_internal/entry.py +44 -8
- meerschaum/_internal/shell/Shell.py +113 -19
- meerschaum/_internal/shell/__init__.py +4 -1
- meerschaum/_internal/static.py +3 -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 +7 -2
- meerschaum/api/_oauth2.py +47 -4
- meerschaum/api/dash/callbacks/dashboard.py +103 -97
- meerschaum/api/dash/callbacks/jobs.py +3 -2
- meerschaum/api/dash/callbacks/login.py +10 -1
- meerschaum/api/dash/callbacks/pipes.py +136 -57
- meerschaum/api/dash/callbacks/register.py +9 -2
- meerschaum/api/dash/callbacks/tokens.py +2 -1
- meerschaum/api/dash/components.py +6 -7
- meerschaum/api/dash/keys.py +17 -1
- meerschaum/api/dash/pages/login.py +2 -2
- meerschaum/api/dash/pages/pipes.py +14 -4
- meerschaum/api/dash/pipes.py +186 -65
- meerschaum/api/dash/tokens.py +1 -1
- 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 +20 -10
- 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 +98 -79
- meerschaum/connectors/sql/_users.py +8 -1
- meerschaum/connectors/sql/tables/__init__.py +20 -3
- meerschaum/connectors/valkey/_ValkeyConnector.py +3 -3
- meerschaum/connectors/valkey/_pipes.py +7 -5
- meerschaum/core/Pipe/__init__.py +62 -72
- 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 +135 -35
- meerschaum/jobs/systemd.py +7 -2
- meerschaum/plugins/__init__.py +277 -81
- meerschaum/utils/_get_pipes.py +30 -4
- meerschaum/utils/daemon/Daemon.py +195 -41
- 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/_pprint.py +1 -0
- meerschaum/utils/formatting/_shell.py +16 -6
- meerschaum/utils/misc.py +18 -38
- meerschaum/utils/packages/__init__.py +15 -13
- meerschaum/utils/packages/_packages.py +1 -0
- meerschaum/utils/pipes.py +39 -7
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +171 -144
- 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.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/METADATA +3 -1
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/RECORD +125 -119
- meerschaum/config/_environment.py +0 -145
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/WHEEL +0 -0
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/entry_points.txt +0 -0
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/licenses/NOTICE +0 -0
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/top_level.txt +0 -0
- {meerschaum-3.0.0rc3.dist-info → meerschaum-3.0.0rc7.dist-info}/zip-safe +0 -0
meerschaum/config/_edit.py
CHANGED
@@ -103,19 +103,23 @@ def write_config(
|
|
103
103
|
if directory is None:
|
104
104
|
from meerschaum.config._paths import CONFIG_DIR_PATH
|
105
105
|
directory = CONFIG_DIR_PATH
|
106
|
+
|
107
|
+
from meerschaum.config import _allow_write_missing
|
106
108
|
from meerschaum._internal.static import STATIC_CONFIG
|
107
109
|
from meerschaum.config._default import default_header_comment
|
108
|
-
from meerschaum.config.
|
109
|
-
from meerschaum.config._read_config import get_keyfile_path
|
110
|
-
from meerschaum.utils.debug import dprint
|
110
|
+
from meerschaum.config._read_config import get_keyfile_path, revert_symlinks_config
|
111
111
|
from meerschaum.utils.yaml import yaml
|
112
112
|
from meerschaum.utils.misc import filter_keywords
|
113
|
-
import json
|
113
|
+
import json
|
114
|
+
import os
|
114
115
|
if config_dict is None:
|
115
116
|
from meerschaum.config import _config
|
116
|
-
cf = _config()
|
117
|
+
cf = _config(allow_replaced=False)
|
117
118
|
config_dict = cf
|
118
119
|
|
120
|
+
if not _allow_write_missing:
|
121
|
+
return False
|
122
|
+
|
119
123
|
default_filetype = STATIC_CONFIG['config']['default_filetype']
|
120
124
|
filetype_dumpers = {
|
121
125
|
'yml' : yaml.dump,
|
@@ -123,9 +127,7 @@ def write_config(
|
|
123
127
|
'json' : json.dump,
|
124
128
|
}
|
125
129
|
|
126
|
-
|
127
|
-
symlinks = config_dict.pop(symlinks_key) if symlinks_key in config_dict else {}
|
128
|
-
config_dict = apply_patch_to_config(config_dict, symlinks)
|
130
|
+
config_dict = revert_symlinks_config(config_dict)
|
129
131
|
|
130
132
|
def determine_filetype(k, v):
|
131
133
|
if k == 'meerschaum':
|
meerschaum/config/_formatting.py
CHANGED
meerschaum/config/_patch.py
CHANGED
@@ -9,11 +9,12 @@ Functions for patching the configuration dictionary
|
|
9
9
|
import sys
|
10
10
|
import copy
|
11
11
|
from meerschaum.utils.typing import Dict, Any
|
12
|
-
from meerschaum.utils.warnings import warn
|
12
|
+
from meerschaum.utils.warnings import warn as _warn
|
13
13
|
|
14
14
|
def apply_patch_to_config(
|
15
15
|
config: Dict[str, Any],
|
16
16
|
patch: Dict[str, Any],
|
17
|
+
warn: bool = False,
|
17
18
|
) -> Dict[str, Any]:
|
18
19
|
"""Patch the config dict with a new dict (cascade patching)."""
|
19
20
|
_base = copy.deepcopy(config) if isinstance(config, dict) else {}
|
@@ -24,7 +25,8 @@ def apply_patch_to_config(
|
|
24
25
|
if base is None:
|
25
26
|
return {}
|
26
27
|
if not isinstance(base, dict):
|
27
|
-
warn
|
28
|
+
if warn:
|
29
|
+
_warn(f"Overwriting the value {base} with a dictionary:\n{patch}")
|
28
30
|
base = {}
|
29
31
|
for key, value in patch.items():
|
30
32
|
if isinstance(value, dict):
|
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
|
|
@@ -410,13 +422,12 @@ def revert_symlinks_config(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
410
422
|
A configuration dictionary with `_symlinks` re-applied.
|
411
423
|
"""
|
412
424
|
from meerschaum.config import apply_patch_to_config
|
413
|
-
from meerschaum._internal.static import STATIC_CONFIG
|
414
425
|
symlinks_key = STATIC_CONFIG['config']['symlinks_key']
|
415
426
|
if symlinks_key not in config:
|
416
427
|
return config
|
417
428
|
|
418
429
|
symlinks_config = config[symlinks_key]
|
419
|
-
reverted_config = apply_patch_to_config(config,
|
430
|
+
reverted_config = apply_patch_to_config(symlinks_config, config, warn=False)
|
420
431
|
_ = reverted_config.pop(symlinks_key, None)
|
421
432
|
return reverted_config
|
422
433
|
|
@@ -462,7 +473,6 @@ def get_keyfile_path(
|
|
462
473
|
)
|
463
474
|
except IndexError:
|
464
475
|
if create_new:
|
465
|
-
from meerschaum._internal.static import STATIC_CONFIG
|
466
476
|
default_filetype = STATIC_CONFIG['config']['default_filetype']
|
467
477
|
return pathlib.Path(os.path.join(directory, key + '.' + default_filetype))
|
468
478
|
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']
|