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/api/routes/_jobs.py
CHANGED
@@ -54,7 +54,7 @@ def _get_job(name: str, sysargs: Union[str, List[str], None] = None):
|
|
54
54
|
|
55
55
|
@app.get(endpoints['jobs'], tags=['Jobs'])
|
56
56
|
def get_jobs(
|
57
|
-
curr_user=fastapi.
|
57
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:read'])),
|
58
58
|
) -> Dict[str, Dict[str, Any]]:
|
59
59
|
"""
|
60
60
|
Return metadata about the current jobs.
|
@@ -83,7 +83,7 @@ def get_jobs(
|
|
83
83
|
@app.get(endpoints['jobs'] + '/{name}', tags=['Jobs'])
|
84
84
|
def get_job(
|
85
85
|
name: str,
|
86
|
-
curr_user=fastapi.
|
86
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:read'])),
|
87
87
|
) -> Dict[str, Any]:
|
88
88
|
"""
|
89
89
|
Return metadata for a single job.
|
@@ -134,7 +134,7 @@ def clean_sysargs(sysargs: List[str]) -> List[str]:
|
|
134
134
|
def create_job(
|
135
135
|
name: str,
|
136
136
|
metadata: Union[List[str], Dict[str, Any]],
|
137
|
-
curr_user=fastapi.
|
137
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:execute', 'jobs:write'])),
|
138
138
|
) -> SuccessTuple:
|
139
139
|
"""
|
140
140
|
Create and start a new job.
|
@@ -167,7 +167,7 @@ def create_job(
|
|
167
167
|
@app.delete(endpoints['jobs'] + '/{name}', tags=['Jobs'])
|
168
168
|
def delete_job(
|
169
169
|
name: str,
|
170
|
-
curr_user=fastapi.
|
170
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:delete'])),
|
171
171
|
) -> SuccessTuple:
|
172
172
|
"""
|
173
173
|
Delete a job.
|
@@ -182,7 +182,7 @@ def delete_job(
|
|
182
182
|
@app.get(endpoints['jobs'] + '/{name}/exists', tags=['Jobs'])
|
183
183
|
def get_job_exists(
|
184
184
|
name: str,
|
185
|
-
curr_user=fastapi.
|
185
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:read'])),
|
186
186
|
) -> bool:
|
187
187
|
"""
|
188
188
|
Return whether a job exists.
|
@@ -194,7 +194,7 @@ def get_job_exists(
|
|
194
194
|
@app.get(endpoints['logs'] + '/{name}', tags=['Jobs'])
|
195
195
|
def get_logs(
|
196
196
|
name: str,
|
197
|
-
curr_user=fastapi.
|
197
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:read', 'logs:read'])),
|
198
198
|
) -> Union[str, None]:
|
199
199
|
"""
|
200
200
|
Return a job's log text.
|
@@ -213,7 +213,7 @@ def get_logs(
|
|
213
213
|
@app.post(endpoints['jobs'] + '/{name}/start', tags=['Jobs'])
|
214
214
|
def start_job(
|
215
215
|
name: str,
|
216
|
-
curr_user=fastapi.
|
216
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:execute'])),
|
217
217
|
) -> SuccessTuple:
|
218
218
|
"""
|
219
219
|
Start a job if stopped.
|
@@ -234,7 +234,7 @@ def start_job(
|
|
234
234
|
@app.post(endpoints['jobs'] + '/{name}/stop', tags=['Jobs'])
|
235
235
|
def stop_job(
|
236
236
|
name: str,
|
237
|
-
curr_user=fastapi.
|
237
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:execute', 'josb:stop'])),
|
238
238
|
) -> SuccessTuple:
|
239
239
|
"""
|
240
240
|
Stop a job if running.
|
@@ -255,7 +255,7 @@ def stop_job(
|
|
255
255
|
@app.post(endpoints['jobs'] + '/{name}/pause', tags=['Jobs'])
|
256
256
|
def pause_job(
|
257
257
|
name: str,
|
258
|
-
curr_user=fastapi.
|
258
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:execute', 'jobs:pause'])),
|
259
259
|
) -> SuccessTuple:
|
260
260
|
"""
|
261
261
|
Pause a job if running.
|
@@ -276,7 +276,7 @@ def pause_job(
|
|
276
276
|
@app.get(endpoints['jobs'] + '/{name}/stop_time', tags=['Jobs'])
|
277
277
|
def get_stop_time(
|
278
278
|
name: str,
|
279
|
-
curr_user=fastapi.
|
279
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:read'])),
|
280
280
|
) -> Union[datetime, None]:
|
281
281
|
"""
|
282
282
|
Get the timestamp when the job was manually stopped.
|
@@ -288,7 +288,7 @@ def get_stop_time(
|
|
288
288
|
@app.get(endpoints['jobs'] + '/{name}/is_blocking_on_stdin', tags=['Jobs'])
|
289
289
|
def get_is_blocking_on_stdin(
|
290
290
|
name: str,
|
291
|
-
curr_user=fastapi.
|
291
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:read'])),
|
292
292
|
) -> bool:
|
293
293
|
"""
|
294
294
|
Return whether a job is blocking on stdin.
|
@@ -297,6 +297,18 @@ def get_is_blocking_on_stdin(
|
|
297
297
|
return job.is_blocking_on_stdin()
|
298
298
|
|
299
299
|
|
300
|
+
@app.get(endpoints['jobs'] + '{name}/prompt_kwargs', tags=['Jobs'])
|
301
|
+
def get_prompt_kwargs(
|
302
|
+
name: str,
|
303
|
+
curr_user=fastapi.Security(ScopedAuth(['jobs:read'])),
|
304
|
+
) -> Dict[str, Any]:
|
305
|
+
"""
|
306
|
+
Return the kwargs for the blocking `prompt`, if available.
|
307
|
+
"""
|
308
|
+
job = _get_job(name)
|
309
|
+
return job.get_prompt_kwargs()
|
310
|
+
|
311
|
+
|
300
312
|
_job_clients = defaultdict(lambda: [])
|
301
313
|
_job_stop_events = defaultdict(lambda: asyncio.Event())
|
302
314
|
_job_queues = defaultdict(lambda: asyncio.Queue())
|
meerschaum/api/routes/_login.py
CHANGED
@@ -16,22 +16,69 @@ from fastapi_login.exceptions import InvalidCredentialsException
|
|
16
16
|
from fastapi.exceptions import RequestValidationError
|
17
17
|
from starlette.responses import JSONResponse
|
18
18
|
|
19
|
-
|
19
|
+
import meerschaum as mrsm
|
20
|
+
from meerschaum.api import (
|
21
|
+
endpoints,
|
22
|
+
get_api_connector,
|
23
|
+
get_cache_connector,
|
24
|
+
app,
|
25
|
+
debug,
|
26
|
+
manager,
|
27
|
+
no_auth,
|
28
|
+
)
|
20
29
|
from meerschaum.core import User
|
21
30
|
from meerschaum._internal.static import STATIC_CONFIG
|
22
31
|
from meerschaum.utils.typing import Dict, Any
|
23
|
-
from meerschaum.utils.misc import is_uuid
|
32
|
+
from meerschaum.utils.misc import is_uuid, is_int
|
24
33
|
from meerschaum.core.User import verify_password
|
25
34
|
from meerschaum.utils.warnings import warn
|
26
35
|
from meerschaum.api._oauth2 import CustomOAuth2PasswordRequestForm
|
27
36
|
|
28
37
|
|
38
|
+
USER_ID_CACHE_EXPIRES_SECONDS: int = mrsm.get_config('system', 'api', 'cache', 'session_expires_minutes') * 60
|
39
|
+
_active_user_ids = {}
|
40
|
+
|
41
|
+
|
29
42
|
@manager.user_loader()
|
30
|
-
def
|
43
|
+
def load_user_or_token_from_manager(username_or_token_id: str) -> User:
|
31
44
|
"""
|
32
45
|
Create the `meerschaum.core.User` object from the username.
|
33
46
|
"""
|
34
|
-
|
47
|
+
cache_conn = get_cache_connector()
|
48
|
+
api_conn = get_api_connector()
|
49
|
+
|
50
|
+
is_token = is_uuid(username_or_token_id)
|
51
|
+
|
52
|
+
if is_token:
|
53
|
+
return api_conn.get_token(username_or_token_id)
|
54
|
+
|
55
|
+
username = username_or_token_id
|
56
|
+
|
57
|
+
cached_user_id = (
|
58
|
+
_active_user_ids.get(username)
|
59
|
+
if cache_conn is None
|
60
|
+
else cache_conn.get(f'mrsm:users:{username}:id')
|
61
|
+
)
|
62
|
+
if isinstance(cached_user_id, str):
|
63
|
+
if is_int(cached_user_id):
|
64
|
+
cached_user_id = int(cached_user_id)
|
65
|
+
elif is_uuid(cached_user_id):
|
66
|
+
cached_user_id = uuid.UUID(cached_user_id)
|
67
|
+
|
68
|
+
user = User(username, instance=api_conn, user_id=cached_user_id)
|
69
|
+
|
70
|
+
if cached_user_id is not None:
|
71
|
+
return user
|
72
|
+
|
73
|
+
user_id = api_conn.get_user_id(user)
|
74
|
+
if user_id is not None:
|
75
|
+
user._user_id = user_id
|
76
|
+
if cache_conn is not None:
|
77
|
+
cache_conn.set(f'mrsm:users:{username}:id', str(user_id), ex=USER_ID_CACHE_EXPIRES_SECONDS)
|
78
|
+
else:
|
79
|
+
_active_user_ids[username] = user_id
|
80
|
+
return user
|
81
|
+
|
35
82
|
|
36
83
|
|
37
84
|
@app.post(endpoints['login'], tags=['Users'])
|
@@ -63,7 +110,10 @@ def login(
|
|
63
110
|
else 'client_credentials'
|
64
111
|
)
|
65
112
|
|
113
|
+
allowed_scopes = []
|
114
|
+
type_ = None
|
66
115
|
expires_dt: Union[datetime, None] = None
|
116
|
+
sub_id = None
|
67
117
|
if grant_type == 'password':
|
68
118
|
user = User(str(username), str(password), instance=get_api_connector())
|
69
119
|
correct_password = no_auth or verify_password(
|
@@ -73,6 +123,10 @@ def login(
|
|
73
123
|
if not correct_password:
|
74
124
|
raise InvalidCredentialsException
|
75
125
|
|
126
|
+
allowed_scopes = user.get_scopes(debug=debug)
|
127
|
+
type_ = get_api_connector().get_user_type(user, debug=debug)
|
128
|
+
sub_id = username
|
129
|
+
|
76
130
|
elif grant_type == 'client_credentials':
|
77
131
|
if not is_uuid(str(client_id)):
|
78
132
|
raise InvalidCredentialsException
|
@@ -83,15 +137,29 @@ def login(
|
|
83
137
|
)
|
84
138
|
if not correct_password:
|
85
139
|
raise InvalidCredentialsException
|
140
|
+
|
141
|
+
allowed_scopes = get_api_connector().get_token_scopes(token_id, debug=debug)
|
142
|
+
sub_id = client_id
|
143
|
+
|
86
144
|
else:
|
87
145
|
raise InvalidCredentialsException
|
88
146
|
|
147
|
+
requested_scopes = data.scope.split()
|
148
|
+
if '*' in allowed_scopes:
|
149
|
+
final_scopes = requested_scopes or ['*']
|
150
|
+
else:
|
151
|
+
final_scopes = [
|
152
|
+
s for s in requested_scopes if s in allowed_scopes
|
153
|
+
] if requested_scopes else allowed_scopes
|
154
|
+
|
89
155
|
expires_minutes = STATIC_CONFIG['api']['oauth']['token_expires_minutes']
|
90
156
|
expires_delta = timedelta(minutes=expires_minutes)
|
91
157
|
expires_dt = datetime.now(timezone.utc).replace(tzinfo=None) + expires_delta
|
92
158
|
access_token = manager.create_access_token(
|
93
159
|
data={
|
94
|
-
'sub':
|
160
|
+
'sub': sub_id,
|
161
|
+
'scopes': final_scopes,
|
162
|
+
'type': type_,
|
95
163
|
},
|
96
164
|
expires=expires_delta
|
97
165
|
)
|
meerschaum/api/routes/_pipes.py
CHANGED
@@ -25,6 +25,7 @@ from meerschaum.api import (
|
|
25
25
|
_get_pipes,
|
26
26
|
debug,
|
27
27
|
ScopedAuth,
|
28
|
+
manager,
|
28
29
|
)
|
29
30
|
from meerschaum.models import (
|
30
31
|
ConnectorKeysModel,
|
@@ -148,7 +149,7 @@ def delete_pipe(
|
|
148
149
|
location_key: str,
|
149
150
|
instance_keys: Optional[str] = None,
|
150
151
|
curr_user = fastapi.Security(ScopedAuth(['pipes:delete'])),
|
151
|
-
) -> SuccessTuple:
|
152
|
+
) -> mrsm.SuccessTuple:
|
152
153
|
"""
|
153
154
|
Delete a Pipe (without dropping its table).
|
154
155
|
"""
|
@@ -187,7 +188,10 @@ async def fetch_pipes_keys(
|
|
187
188
|
tags=json.loads(tags),
|
188
189
|
params=json.loads(params),
|
189
190
|
)
|
190
|
-
return
|
191
|
+
return [
|
192
|
+
(keys_tuple[0], keys_tuple[1], keys_tuple[2])
|
193
|
+
for keys_tuple in keys
|
194
|
+
]
|
191
195
|
|
192
196
|
|
193
197
|
@app.get(pipes_endpoint, tags=['Pipes: Attributes'])
|
@@ -197,7 +201,6 @@ async def get_pipes(
|
|
197
201
|
location_keys: str = "",
|
198
202
|
instance_keys: str = "",
|
199
203
|
curr_user=fastapi.Security(ScopedAuth(['pipes:read'])),
|
200
|
-
debug: bool = False,
|
201
204
|
) -> PipesWithParametersDictModel:
|
202
205
|
"""
|
203
206
|
Get all registered Pipes with metadata, excluding parameters.
|
@@ -384,7 +387,6 @@ async def sync_pipe(
|
|
384
387
|
workers: Optional[int] = None,
|
385
388
|
columns: Optional[str] = None,
|
386
389
|
curr_user = fastapi.Security(ScopedAuth(['pipes:write'])),
|
387
|
-
debug: bool = False,
|
388
390
|
) -> mrsm.SuccessTuple:
|
389
391
|
"""
|
390
392
|
Add data to an existing Pipe.
|
@@ -8,7 +8,7 @@ Routes to the Webterm proxy.
|
|
8
8
|
|
9
9
|
import asyncio
|
10
10
|
from meerschaum.utils.typing import Optional
|
11
|
-
from meerschaum.api import app, endpoints
|
11
|
+
from meerschaum.api import app, endpoints, webterm_port
|
12
12
|
from meerschaum.utils.packages import attempt_import
|
13
13
|
from meerschaum.api.dash.sessions import is_session_authenticated, get_username_from_session
|
14
14
|
fastapi, fastapi_responses = attempt_import('fastapi', 'fastapi.responses')
|
@@ -71,7 +71,7 @@ async def get_webterm(
|
|
71
71
|
|
72
72
|
username = get_username_from_session(session_id)
|
73
73
|
async with httpx.AsyncClient() as client:
|
74
|
-
webterm_url = f"http://localhost:
|
74
|
+
webterm_url = f"http://localhost:{webterm_port}/webterm/{username or session_id}"
|
75
75
|
response = await client.get(webterm_url)
|
76
76
|
text = response.text
|
77
77
|
if request.url.scheme == 'https':
|
@@ -100,7 +100,7 @@ async def webterm_websocket(websocket: WebSocket, session_id: str):
|
|
100
100
|
|
101
101
|
username = get_username_from_session(session_id)
|
102
102
|
|
103
|
-
ws_url = f"ws://localhost:
|
103
|
+
ws_url = f"ws://localhost:{webterm_port}/websocket/{username or session_id}"
|
104
104
|
async with websockets.connect(ws_url) as ws:
|
105
105
|
async def forward_messages():
|
106
106
|
try:
|
meerschaum/config/__init__.py
CHANGED
@@ -13,8 +13,9 @@ import os
|
|
13
13
|
import shutil
|
14
14
|
import sys
|
15
15
|
import copy
|
16
|
+
import contextlib
|
16
17
|
|
17
|
-
from meerschaum.utils.typing import Any, Dict, Optional
|
18
|
+
from meerschaum.utils.typing import Any, Dict, Optional, Union
|
18
19
|
from meerschaum.utils.threading import RLock
|
19
20
|
|
20
21
|
from meerschaum.config._version import __version__
|
@@ -43,6 +44,7 @@ __all__ = (
|
|
43
44
|
'write_config',
|
44
45
|
'edit_config',
|
45
46
|
'set_config',
|
47
|
+
'replace_config',
|
46
48
|
'search_and_substitute_config',
|
47
49
|
'revert_symlinks_config',
|
48
50
|
'get_possible_keys',
|
@@ -57,17 +59,23 @@ _locks = {'config': RLock()}
|
|
57
59
|
|
58
60
|
### apply config preprocessing (e.g. main to meta)
|
59
61
|
config = {}
|
62
|
+
_backup_config = None
|
63
|
+
_allow_write_missing: bool = True
|
64
|
+
|
65
|
+
|
60
66
|
def _config(
|
61
67
|
*keys: str,
|
62
68
|
reload: bool = False,
|
63
69
|
substitute: bool = True,
|
64
70
|
sync_files: bool = True,
|
71
|
+
allow_replaced: bool = True,
|
65
72
|
write_missing: bool = True,
|
66
73
|
) -> Dict[str, Any]:
|
67
74
|
"""
|
68
75
|
Read and process the configuration file.
|
69
76
|
"""
|
70
|
-
global config
|
77
|
+
global config, _backup_config
|
78
|
+
|
71
79
|
if config is None or reload:
|
72
80
|
with _locks['config']:
|
73
81
|
config = {}
|
@@ -77,12 +85,16 @@ def _config(
|
|
77
85
|
key_config = read_config(
|
78
86
|
keys = [keys[0]],
|
79
87
|
substitute = substitute,
|
80
|
-
write_missing = write_missing,
|
88
|
+
write_missing = write_missing and _allow_write_missing,
|
81
89
|
)
|
82
90
|
if keys[0] in key_config:
|
83
91
|
config[keys[0]] = key_config[keys[0]]
|
84
|
-
if sync_files:
|
92
|
+
if sync_files and _allow_write_missing:
|
85
93
|
_sync_files(keys=[keys[0] if keys else None])
|
94
|
+
|
95
|
+
if not allow_replaced:
|
96
|
+
return _backup_config if _backup_config is not None else config
|
97
|
+
|
86
98
|
return config
|
87
99
|
|
88
100
|
|
@@ -158,7 +170,11 @@ def get_config(
|
|
158
170
|
dprint(f"Indexing keys: {keys}", color=False)
|
159
171
|
|
160
172
|
if len(keys) == 0:
|
161
|
-
_rc = _config(
|
173
|
+
_rc = _config(
|
174
|
+
substitute=substitute,
|
175
|
+
sync_files=sync_files,
|
176
|
+
write_missing=(write_missing and _allow_write_missing),
|
177
|
+
)
|
162
178
|
if as_tuple:
|
163
179
|
return True, _rc
|
164
180
|
return _rc
|
@@ -177,15 +193,15 @@ def get_config(
|
|
177
193
|
except Exception:
|
178
194
|
import traceback
|
179
195
|
traceback.print_exc()
|
196
|
+
_subbed = {keys[0]: config[keys[0]]}
|
197
|
+
|
180
198
|
config[keys[0]] = _subbed[keys[0]]
|
181
199
|
if symlinks_key in _subbed:
|
182
200
|
if symlinks_key not in config:
|
183
201
|
config[symlinks_key] = {}
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
_subbed,
|
188
|
-
{symlinks_key: config[symlinks_key][keys[0]]}
|
202
|
+
config[symlinks_key] = apply_patch_to_config(
|
203
|
+
_subbed.get(symlinks_key, {}),
|
204
|
+
config.get(symlinks_key, {}),
|
189
205
|
)
|
190
206
|
|
191
207
|
from meerschaum.config._sync import sync_files as _sync_files
|
@@ -312,13 +328,44 @@ def write_plugin_config(
|
|
312
328
|
return write_config(cf, **kw)
|
313
329
|
|
314
330
|
|
331
|
+
@contextlib.contextmanager
|
332
|
+
def replace_config(config_: Union[Dict[str, Any], None]):
|
333
|
+
"""
|
334
|
+
Temporarily override the Meerschaum config dictionary.
|
335
|
+
|
336
|
+
Parameters
|
337
|
+
----------
|
338
|
+
config_: Dict[str, Any]
|
339
|
+
The new config dictionary to temporarily replace the canonical `config`.
|
340
|
+
"""
|
341
|
+
if config_ is None:
|
342
|
+
try:
|
343
|
+
yield
|
344
|
+
finally:
|
345
|
+
return
|
346
|
+
|
347
|
+
global _backup_config, _allow_write_missing
|
348
|
+
|
349
|
+
_backup_config = _config()
|
350
|
+
_allow_write_missing = False
|
351
|
+
set_config(config_)
|
352
|
+
|
353
|
+
try:
|
354
|
+
yield
|
355
|
+
finally:
|
356
|
+
set_config(_backup_config)
|
357
|
+
_allow_write_missing = True
|
358
|
+
|
315
359
|
### This need to be below get_config to avoid a circular import.
|
316
360
|
from meerschaum.config._read_config import read_config
|
317
361
|
|
318
362
|
### If environment variable MRSM_CONFIG or MRSM_PATCH is set, patch config before anything else.
|
319
|
-
from meerschaum.config.
|
320
|
-
|
321
|
-
|
363
|
+
from meerschaum.config.environment import (
|
364
|
+
apply_environment_patches as _apply_environment_patches,
|
365
|
+
apply_environment_uris as _apply_environment_uris,
|
366
|
+
)
|
367
|
+
_apply_environment_uris()
|
368
|
+
_apply_environment_patches()
|
322
369
|
|
323
370
|
|
324
371
|
from meerschaum.config._paths import PATCH_DIR_PATH, PERMANENT_PATCH_DIR_PATH
|
meerschaum/config/_default.py
CHANGED
@@ -36,8 +36,7 @@ CONNECTOR_ATTRIBUTES: Dict[str, Dict[str, Any]] = {
|
|
36
36
|
default_meerschaum_config = {
|
37
37
|
'instance': 'sql:main',
|
38
38
|
'api_instance': 'MRSM{meerschaum:instance}',
|
39
|
-
'
|
40
|
-
'default_repository': 'api:mrsm',
|
39
|
+
'repository': 'api:mrsm',
|
41
40
|
'connectors': {
|
42
41
|
'sql': {
|
43
42
|
'default': {},
|
@@ -51,7 +50,7 @@ default_meerschaum_config = {
|
|
51
50
|
},
|
52
51
|
'local': {
|
53
52
|
'flavor': 'sqlite',
|
54
|
-
'database': SQLITE_DB_PATH
|
53
|
+
'database': "{SQLITE_DB_PATH}",
|
55
54
|
},
|
56
55
|
'memory': {
|
57
56
|
'flavor': 'sqlite',
|
@@ -117,75 +116,107 @@ default_system_config = {
|
|
117
116
|
'connect_args': {},
|
118
117
|
},
|
119
118
|
},
|
120
|
-
|
121
119
|
'api': {
|
122
120
|
},
|
123
121
|
},
|
124
|
-
|
125
|
-
|
126
|
-
'
|
127
|
-
'
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
'
|
132
|
-
'
|
133
|
-
'
|
134
|
-
|
135
|
-
|
136
|
-
'
|
137
|
-
'
|
122
|
+
'api': 'MRSM{api}',
|
123
|
+
'webterm': 'MRSM{api:webterm}',
|
124
|
+
'cli': {
|
125
|
+
'max_daemons': (multiprocessing.cpu_count() * 3),
|
126
|
+
'refresh_seconds': 0.1,
|
127
|
+
'allowed_prefixes': ['*'],
|
128
|
+
'disallowed_prefixes': [
|
129
|
+
'edit',
|
130
|
+
'start daemon',
|
131
|
+
'start job',
|
132
|
+
'stop daemon',
|
133
|
+
'show daemon',
|
134
|
+
'restart daemon',
|
135
|
+
'reload',
|
136
|
+
'start worker',
|
137
|
+
'start job',
|
138
|
+
'python',
|
139
|
+
'login',
|
140
|
+
'executor',
|
141
|
+
'os ',
|
142
|
+
'sh ',
|
143
|
+
'start api',
|
144
|
+
'start webterm',
|
145
|
+
'stack',
|
146
|
+
'instance',
|
147
|
+
'debug',
|
148
|
+
'bootstrap',
|
149
|
+
'daemon',
|
150
|
+
],
|
151
|
+
},
|
152
|
+
'experimental': {
|
153
|
+
'fetch': False,
|
154
|
+
'cache': True,
|
155
|
+
'space': False,
|
156
|
+
'join_fetch': False,
|
157
|
+
'inplace_sync': True,
|
158
|
+
'uv_pip': True,
|
159
|
+
'systemd_healthcheck': False,
|
160
|
+
'valkey_session_cache': True,
|
161
|
+
'cli_daemon': True,
|
162
|
+
},
|
163
|
+
}
|
164
|
+
|
165
|
+
default_api_config = {
|
166
|
+
'uvicorn': {
|
167
|
+
'app': 'meerschaum.api:app',
|
168
|
+
'port': 8000,
|
169
|
+
'host': '0.0.0.0',
|
170
|
+
'workers': max(int(multiprocessing.cpu_count() / 2), 1),
|
171
|
+
'proxy_headers': True,
|
172
|
+
'forwarded_allow_ips': '*',
|
173
|
+
},
|
174
|
+
'cache': {
|
175
|
+
'connector': 'valkey:main',
|
176
|
+
'session_expires_minutes': 43200,
|
177
|
+
},
|
178
|
+
'data': {
|
179
|
+
'max_response_row_limit': 100_000,
|
180
|
+
'chunks': {
|
181
|
+
'ttl_seconds': 1800,
|
138
182
|
},
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
183
|
+
},
|
184
|
+
'endpoints': {
|
185
|
+
'docs_in_production': True,
|
186
|
+
},
|
187
|
+
'tokens': {
|
188
|
+
'valid_refresh_minutes': 60,
|
189
|
+
'default_expiration_days': 366,
|
190
|
+
},
|
191
|
+
'permissions': {
|
192
|
+
'registration': {
|
193
|
+
'users': True,
|
194
|
+
'pipes': True,
|
195
|
+
'plugins': True,
|
144
196
|
},
|
145
|
-
'
|
146
|
-
'
|
197
|
+
'actions': {
|
198
|
+
'non_admin': True,
|
147
199
|
},
|
148
|
-
'
|
149
|
-
'
|
150
|
-
'
|
200
|
+
'chaining': {
|
201
|
+
'insecure_parent_instance': False,
|
202
|
+
'child_apis': False,
|
151
203
|
},
|
152
|
-
'
|
153
|
-
'
|
154
|
-
|
155
|
-
'pipes': True,
|
156
|
-
'plugins': True,
|
157
|
-
},
|
158
|
-
'actions': {
|
159
|
-
'non_admin': True,
|
160
|
-
},
|
161
|
-
'chaining': {
|
162
|
-
'insecure_parent_instance': False,
|
163
|
-
'child_apis': False,
|
164
|
-
},
|
165
|
-
'instances': {
|
166
|
-
'allow_multiple_instances': True,
|
167
|
-
'allowed_instance_keys': ['*']
|
168
|
-
},
|
204
|
+
'instances': {
|
205
|
+
'allow_multiple_instances': True,
|
206
|
+
'allowed_instance_keys': ['*']
|
169
207
|
},
|
170
|
-
'protocol': default_meerschaum_config['connectors']['api']['default']['protocol'],
|
171
208
|
},
|
209
|
+
'protocol': default_meerschaum_config['connectors']['api']['default']['protocol'],
|
172
210
|
'webterm': {
|
173
211
|
'tmux': {
|
174
212
|
'enabled': True,
|
175
213
|
'session_suffix': '_mrsm',
|
176
214
|
},
|
177
|
-
|
178
|
-
|
179
|
-
'fetch': False,
|
180
|
-
'cache': True,
|
181
|
-
'space': False,
|
182
|
-
'join_fetch': False,
|
183
|
-
'inplace_sync': True,
|
184
|
-
'uv_pip': True,
|
185
|
-
'systemd_healthcheck': False,
|
186
|
-
'valkey_session_cache': True,
|
215
|
+
'host': '127.0.0.1',
|
216
|
+
'port': 8765,
|
187
217
|
},
|
188
218
|
}
|
219
|
+
|
189
220
|
default_pipes_config = {
|
190
221
|
'parameters': {
|
191
222
|
'columns': {
|
@@ -221,15 +252,12 @@ default_pipes_config = {
|
|
221
252
|
},
|
222
253
|
}
|
223
254
|
default_plugins_config = {}
|
224
|
-
default_experimental_config = {
|
225
|
-
'venv': True,
|
226
|
-
}
|
227
|
-
|
228
255
|
|
229
256
|
### build default config dictionary
|
230
257
|
default_config = {}
|
231
258
|
default_config['meerschaum'] = default_meerschaum_config
|
232
259
|
default_config['system'] = default_system_config
|
260
|
+
default_config['api'] = default_api_config
|
233
261
|
from meerschaum.config._formatting import default_formatting_config
|
234
262
|
default_config['formatting'] = default_formatting_config
|
235
263
|
from meerschaum.config._shell import default_shell_config
|
@@ -241,7 +269,7 @@ default_config['jobs'] = default_jobs_config
|
|
241
269
|
### add configs from other packages
|
242
270
|
try:
|
243
271
|
import meerschaum.config.stack
|
244
|
-
except ImportError
|
272
|
+
except ImportError:
|
245
273
|
pass
|
246
274
|
finally:
|
247
275
|
from meerschaum.config.stack import default_stack_config
|